Initial commit of source code (#1)
* Initial commit * Got windows projects compiling * First pass as removing un-used code * Moved portable graphics code into Xamarin.Graphics.Core * More renaming. * More renaming. * More renaming. * Fixed shared project issue. * More renaming. * More renaming. * More cleanup/refactoring. * Removed stroke location. * More naming cleanups * More cleanup. * Switched to structs for Size, Rectangle and Point * Cleanup Android project. * Cleanup mac naming. * Fixed iOS naming. * Scrubbed elevenworks from files. * More scrubbing * More cleanup * Renamed files * Renamed files.
This commit is contained in:
Родитель
f6cfdb98a6
Коммит
7af33e22b3
|
@ -348,3 +348,5 @@ MigrationBackup/
|
|||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
.DS_Store
|
||||
.idea
|
||||
|
|
|
@ -1 +1 @@
|
|||
# Xamarin.Graphics
|
||||
# System.Graphics
|
|
@ -0,0 +1,246 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30523.141
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Core", "src\libraries\System.Graphics.Core\System.Graphics.Core.csproj", "{76F3031E-341E-43CC-BD92-B23F83495DB7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Text.Markdig", "src\libraries\System.Graphics.Text.Markdig\System.Graphics.Text.Markdig.csproj", "{ED36FB6D-7278-465D-9C44-D01927258BA1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Text.WPF", "src\libraries\System.Graphics.Text.WPF\System.Graphics.Text.WPF.csproj", "{029785AD-50DB-4DA0-8882-126483BF0F2F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Android", "src\libraries\System.Graphics.Android\System.Graphics.Android.csproj", "{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Blazor", "src\libraries\System.Graphics.Blazor\System.Graphics.Blazor.csproj", "{B755D39B-1AD8-48FA-B56F-53FCA7478085}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Blazor.JS", "src\libraries\System.Graphics.Blazor.JS\System.Graphics.Blazor.JS.csproj", "{0E1E4EF4-9415-43FF-971F-204311403A2D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Forms.Core", "src\libraries\System.Graphics.Forms.Core\System.Graphics.Forms.Core.csproj", "{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Forms.Android", "src\libraries\System.Graphics.Forms.Android\System.Graphics.Forms.Android.csproj", "{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Forms.iOS", "src\libraries\System.Graphics.Forms.iOS\System.Graphics.Forms.iOS.csproj", "{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Forms.Win2D.UWP", "src\libraries\System.Graphics.Forms.Win2D.UWP\System.Graphics.Forms.Win2D.UWP.csproj", "{8B29A316-4194-4051-A9A2-1F55428F6E87}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.GDI.Winforms", "src\libraries\System.Graphics.GDI.Winforms\System.Graphics.GDI.Winforms.csproj", "{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.CoreGraphics.iOS", "src\libraries\System.Graphics.CoreGraphics.iOS\System.Graphics.CoreGraphics.iOS.csproj", "{F8D43F64-AF09-4E06-8473-5BDC37281908}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.CoreGraphics.Mac", "src\libraries\System.Graphics.CoreGraphics.Mac\System.Graphics.CoreGraphics.Mac.csproj", "{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Skia.Core", "src\libraries\System.Graphics.Skia.Core\System.Graphics.Skia.Core.csproj", "{C7613109-6198-402E-953F-57EA81E8238E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Skia.WPF", "src\libraries\System.Graphics.Skia.WPF\System.Graphics.Skia.WPF.csproj", "{28044370-E6F2-43AA-A164-19B142503DAC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Win2D.UWP", "src\libraries\System.Graphics.Win2D.UWP\System.Graphics.Win2D.UWP.csproj", "{20794E60-3BFD-46F4-BC0B-87B3959AA3C7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.Xaml.WPF", "src\libraries\System.Graphics.Xaml.WPF\System.Graphics.Xaml.WPF.csproj", "{7CFA3483-D337-406C-A077-F284CBE6A552}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.Xaml.UWP", "src\libraries\System.Graphics.Xaml.UWP\System.Graphics.Xaml.UWP.csproj", "{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Graphics.SharpDX.UWP", "src\libraries\System.Graphics.SharpDX.UWP\System.Graphics.SharpDX.UWP.csproj", "{694E970D-5C18-483F-BC48-D6D86DA8E78F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Graphics.SharpDX.WPF", "src\libraries\System.Graphics.SharpDX.WPF\System.Graphics.SharpDX.WPF.csproj", "{BC291304-FDC0-4E1D-A673-39F5E446B0E0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D6F0522F-3BB5-4A10-953F-CA047F3D881A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{CE764F51-0699-4446-BEE2-17FBBDC99C5C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.Portable", "src\samples\GraphicsTester.Portable\GraphicsTester.Portable.csproj", "{D1878E17-08A7-4858-94B8-DE8B38BE2632}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphicsTester.Android", "src\samples\GraphicsTester.Android\GraphicsTester.Android.csproj", "{7E256F6E-B676-41D3-B94E-CCDC40AA6749}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphicsTester.GDI", "src\samples\GraphicsTester.GDI\GraphicsTester.GDI.csproj", "{80C34F92-3785-46AC-BDB8-26B3D9DF2F27}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.SharpDX.Windows", "src\samples\GraphicsTester.SharpDX.Windows\GraphicsTester.SharpDX.Windows.csproj", "{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphicsTester.Skia.Mac", "src\samples\GraphicsTester.Skia.Mac\GraphicsTester.Skia.Mac.csproj", "{E78A38E5-57F6-4F31-8A4C-7A1B53679E13}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.Skia.Windows", "src\samples\GraphicsTester.Skia.Windows\GraphicsTester.Skia.Windows.csproj", "{9C362B66-3EBC-4574-9897-77DB85CD0255}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphicsTester.WindowsUniversal", "src\samples\GraphicsTester.WindowsUniversal\GraphicsTester.WindowsUniversal.csproj", "{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.Xaml", "src\samples\GraphicsTester.Xaml\GraphicsTester.Xaml.csproj", "{9365DB5B-E4F4-4E05-8558-DAA01789D7F6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{5FDD2BE9-96B1-4E7D-8028-B8158E6A0FEC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDX.Desktop", "src\external\SharpDX.Desktop\SharpDX.Desktop.csproj", "{D52F4128-20EA-41F3-BA67-0586BBCB3E5A}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Graphics.SharpDX.Shared", "src\libraries\System.Graphics.SharpDX.Shared\System.Graphics.SharpDX.Shared.shproj", "{5163BCB2-4D70-42DA-975A-AF55D6B6A0B3}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Graphics.CoreGraphics", "src\libraries\System.Graphics.CoreGraphics\System.Graphics.CoreGraphics.shproj", "{95A3E5F1-26B7-4BD8-8F01-48FE7BFD79FA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
src\libraries\System.Graphics.SharpDX.Shared\System.Graphics.SharpDX.Shared.projitems*{5163bcb2-4d70-42da-975a-af55d6b6a0b3}*SharedItemsImports = 13
|
||||
src\libraries\System.Graphics.SharpDX.Shared\System.Graphics.SharpDX.Shared.projitems*{694e970d-5c18-483f-bc48-d6d86da8e78f}*SharedItemsImports = 4
|
||||
src\libraries\System.Graphics.CoreGraphics\System.Graphics.CoreGraphics.projitems*{95a3e5f1-26b7-4bd8-8f01-48fe7bfd79fa}*SharedItemsImports = 13
|
||||
src\libraries\System.Graphics.SharpDX.Shared\System.Graphics.SharpDX.Shared.projitems*{bc291304-fdc0-4e1d-a673-39f5e446b0e0}*SharedItemsImports = 5
|
||||
src\libraries\System.Graphics.CoreGraphics\System.Graphics.CoreGraphics.projitems*{edf26ebb-0ac3-4212-81d6-8dd08f5acc17}*SharedItemsImports = 4
|
||||
src\libraries\System.Graphics.CoreGraphics\System.Graphics.CoreGraphics.projitems*{f8d43f64-af09-4e06-8473-5bdc37281908}*SharedItemsImports = 4
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{76F3031E-341E-43CC-BD92-B23F83495DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{76F3031E-341E-43CC-BD92-B23F83495DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{76F3031E-341E-43CC-BD92-B23F83495DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{76F3031E-341E-43CC-BD92-B23F83495DB7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ED36FB6D-7278-465D-9C44-D01927258BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED36FB6D-7278-465D-9C44-D01927258BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ED36FB6D-7278-465D-9C44-D01927258BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ED36FB6D-7278-465D-9C44-D01927258BA1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{029785AD-50DB-4DA0-8882-126483BF0F2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{029785AD-50DB-4DA0-8882-126483BF0F2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{029785AD-50DB-4DA0-8882-126483BF0F2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{029785AD-50DB-4DA0-8882-126483BF0F2F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B755D39B-1AD8-48FA-B56F-53FCA7478085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B755D39B-1AD8-48FA-B56F-53FCA7478085}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B755D39B-1AD8-48FA-B56F-53FCA7478085}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B755D39B-1AD8-48FA-B56F-53FCA7478085}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0E1E4EF4-9415-43FF-971F-204311403A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0E1E4EF4-9415-43FF-971F-204311403A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0E1E4EF4-9415-43FF-971F-204311403A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0E1E4EF4-9415-43FF-971F-204311403A2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B29A316-4194-4051-A9A2-1F55428F6E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B29A316-4194-4051-A9A2-1F55428F6E87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B29A316-4194-4051-A9A2-1F55428F6E87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B29A316-4194-4051-A9A2-1F55428F6E87}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8D43F64-AF09-4E06-8473-5BDC37281908}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8D43F64-AF09-4E06-8473-5BDC37281908}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8D43F64-AF09-4E06-8473-5BDC37281908}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8D43F64-AF09-4E06-8473-5BDC37281908}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C7613109-6198-402E-953F-57EA81E8238E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C7613109-6198-402E-953F-57EA81E8238E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7613109-6198-402E-953F-57EA81E8238E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7613109-6198-402E-953F-57EA81E8238E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{28044370-E6F2-43AA-A164-19B142503DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{28044370-E6F2-43AA-A164-19B142503DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{28044370-E6F2-43AA-A164-19B142503DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{28044370-E6F2-43AA-A164-19B142503DAC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20794E60-3BFD-46F4-BC0B-87B3959AA3C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20794E60-3BFD-46F4-BC0B-87B3959AA3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20794E60-3BFD-46F4-BC0B-87B3959AA3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20794E60-3BFD-46F4-BC0B-87B3959AA3C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7CFA3483-D337-406C-A077-F284CBE6A552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7CFA3483-D337-406C-A077-F284CBE6A552}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7CFA3483-D337-406C-A077-F284CBE6A552}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7CFA3483-D337-406C-A077-F284CBE6A552}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{694E970D-5C18-483F-BC48-D6D86DA8E78F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{694E970D-5C18-483F-BC48-D6D86DA8E78F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{694E970D-5C18-483F-BC48-D6D86DA8E78F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{694E970D-5C18-483F-BC48-D6D86DA8E78F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BC291304-FDC0-4E1D-A673-39F5E446B0E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BC291304-FDC0-4E1D-A673-39F5E446B0E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BC291304-FDC0-4E1D-A673-39F5E446B0E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BC291304-FDC0-4E1D-A673-39F5E446B0E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D1878E17-08A7-4858-94B8-DE8B38BE2632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D1878E17-08A7-4858-94B8-DE8B38BE2632}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D1878E17-08A7-4858-94B8-DE8B38BE2632}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D1878E17-08A7-4858-94B8-DE8B38BE2632}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7E256F6E-B676-41D3-B94E-CCDC40AA6749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7E256F6E-B676-41D3-B94E-CCDC40AA6749}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7E256F6E-B676-41D3-B94E-CCDC40AA6749}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7E256F6E-B676-41D3-B94E-CCDC40AA6749}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{80C34F92-3785-46AC-BDB8-26B3D9DF2F27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{80C34F92-3785-46AC-BDB8-26B3D9DF2F27}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{80C34F92-3785-46AC-BDB8-26B3D9DF2F27}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{80C34F92-3785-46AC-BDB8-26B3D9DF2F27}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E78A38E5-57F6-4F31-8A4C-7A1B53679E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E78A38E5-57F6-4F31-8A4C-7A1B53679E13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E78A38E5-57F6-4F31-8A4C-7A1B53679E13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E78A38E5-57F6-4F31-8A4C-7A1B53679E13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C362B66-3EBC-4574-9897-77DB85CD0255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C362B66-3EBC-4574-9897-77DB85CD0255}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C362B66-3EBC-4574-9897-77DB85CD0255}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C362B66-3EBC-4574-9897-77DB85CD0255}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3}.Debug|Any CPU.Build.0 = Debug|x86
|
||||
{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3}.Release|Any CPU.Build.0 = Release|x86
|
||||
{9365DB5B-E4F4-4E05-8558-DAA01789D7F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9365DB5B-E4F4-4E05-8558-DAA01789D7F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9365DB5B-E4F4-4E05-8558-DAA01789D7F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9365DB5B-E4F4-4E05-8558-DAA01789D7F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D52F4128-20EA-41F3-BA67-0586BBCB3E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D52F4128-20EA-41F3-BA67-0586BBCB3E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D52F4128-20EA-41F3-BA67-0586BBCB3E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D52F4128-20EA-41F3-BA67-0586BBCB3E5A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{76F3031E-341E-43CC-BD92-B23F83495DB7} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{ED36FB6D-7278-465D-9C44-D01927258BA1} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{029785AD-50DB-4DA0-8882-126483BF0F2F} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{F63D421B-A5E5-4107-9BAC-9C45ED1363DE} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{B755D39B-1AD8-48FA-B56F-53FCA7478085} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{0E1E4EF4-9415-43FF-971F-204311403A2D} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{BE7203D5-B616-4F38-9D9F-B5D4D92DBDF3} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{A1FE5CA7-45EE-4BB0-9FDF-8698BB22568B} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{7B6503A2-DA2C-4512-BBA5-95BF85CF63C1} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{8B29A316-4194-4051-A9A2-1F55428F6E87} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{3C3BF90F-C579-463D-B33F-8B9B16D2DAB7} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{F8D43F64-AF09-4E06-8473-5BDC37281908} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{EDF26EBB-0AC3-4212-81D6-8DD08F5ACC17} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{C7613109-6198-402E-953F-57EA81E8238E} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{28044370-E6F2-43AA-A164-19B142503DAC} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{20794E60-3BFD-46F4-BC0B-87B3959AA3C7} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{7CFA3483-D337-406C-A077-F284CBE6A552} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{9DF3C198-5B93-47B1-A8C4-11A25CA6DEF2} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{694E970D-5C18-483F-BC48-D6D86DA8E78F} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{BC291304-FDC0-4E1D-A673-39F5E446B0E0} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{D1878E17-08A7-4858-94B8-DE8B38BE2632} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{7E256F6E-B676-41D3-B94E-CCDC40AA6749} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{80C34F92-3785-46AC-BDB8-26B3D9DF2F27} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{8FD3CD70-75DD-49F7-BDA7-BB7073EBF4A3} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{E78A38E5-57F6-4F31-8A4C-7A1B53679E13} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{9C362B66-3EBC-4574-9897-77DB85CD0255} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{91CD8AD3-E080-4286-8DC7-C2EE5D1625D3} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{9365DB5B-E4F4-4E05-8558-DAA01789D7F6} = {CE764F51-0699-4446-BEE2-17FBBDC99C5C}
|
||||
{D52F4128-20EA-41F3-BA67-0586BBCB3E5A} = {5FDD2BE9-96B1-4E7D-8028-B8158E6A0FEC}
|
||||
{5163BCB2-4D70-42DA-975A-AF55D6B6A0B3} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
{95A3E5F1-26B7-4BD8-8F01-48FE7BFD79FA} = {9E4B772B-D417-4147-B8B1-FD9E3B0B4EC5}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0B07E734-6AE2-4A12-AD60-C072F3695854}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,10 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CG/@EntryIndexedValue">CG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HSL/@EntryIndexedValue">HSL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NS/@EntryIndexedValue">NS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Antialias/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=blurrable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Markdig/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=segoe/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tahoma/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpDX.Desktop.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides <see cref="IEqualityComparer{T}"/> for default value types.
|
||||
/// </summary>
|
||||
internal static class EqualityComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// A default <see cref="IEqualityComparer{T}"/> for <see cref="System.IntPtr"/>.
|
||||
/// </summary>
|
||||
public static readonly IEqualityComparer<IntPtr> DefaultIntPtr = new IntPtrComparer();
|
||||
|
||||
internal class IntPtrComparer : EqualityComparer<IntPtr>
|
||||
{
|
||||
public override bool Equals(IntPtr x, IntPtr y)
|
||||
{
|
||||
return x == y;
|
||||
}
|
||||
|
||||
public override int GetHashCode(IntPtr obj)
|
||||
{
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - SharpDX Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// currently it is working correctly only on Desktop platform
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SharpDX.Desktop.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to allow programmatic capturing of graphics information that can be loaded later in Visual Studio.
|
||||
/// This is a managed implementation of the VsDbg class (http://msdn.microsoft.com/en-us/library/vstudio/dn440549.aspx).
|
||||
/// Requires to have installed VS Remote Tools.
|
||||
/// http://msdn.microsoft.com/en-us/library/vstudio/hh708963.aspx
|
||||
/// http://msdn.microsoft.com/en-us/library/vstudio/hh780905.aspx
|
||||
/// </summary>
|
||||
public sealed class VSGraphicsDebugger : DisposeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper structure to ease the begin/end graphics capturing
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// var debugger = new VSGraphicsDebugger();
|
||||
/// using(debugger.BeginCapture())
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// </example>
|
||||
public struct CaptureToken : IDisposable
|
||||
{
|
||||
private readonly VSGraphicsDebugger debugger;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="CaptureToken"/> structure.
|
||||
/// </summary>
|
||||
/// <param name="debugger">The attanched graphics debugger.</param>
|
||||
internal CaptureToken(VSGraphicsDebugger debugger)
|
||||
: this()
|
||||
{
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the capture by calling <see cref="VSGraphicsDebugger.EndCapture"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
debugger.EndCapture();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="VSGraphicsDebugger"/> class and prepares the in-app component of graphics diagnostics to actively capture and record graphics information..
|
||||
/// </summary>
|
||||
/// <param name="logFileName">The destination filename for log writing.</param>
|
||||
public VSGraphicsDebugger(string logFileName)
|
||||
{
|
||||
VsgDbgInit(logFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of the active graphics log (.vsglog) file into a new file.
|
||||
/// </summary>
|
||||
/// <param name="destinationFileName">The new log file name.</param>
|
||||
public void CopyLogFile(string destinationFileName)
|
||||
{
|
||||
VsgDbgCopy(destinationFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the graphics diagnostics HUD overlay on or off.
|
||||
/// </summary>
|
||||
public void ToggleHUD()
|
||||
{
|
||||
VsgDbgToggleHUD();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a custom message to the graphics diagnostics HUD (Head-Up Display).
|
||||
/// </summary>
|
||||
/// <param name="message">The message to add.</param>
|
||||
public void AddHUDMessage(string message)
|
||||
{
|
||||
VsgDbgAddHUDMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the remainder of the current frame to the graphics log file.
|
||||
/// </summary>
|
||||
public void CaptureCurrentFrame()
|
||||
{
|
||||
VsgDbgCaptureCurrentFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins a capture interval that will end with <see cref="EndCapture"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="CaptureToken"/> instance that once disposed, calls automatically the <see cref="EndCapture"/> method.</returns>
|
||||
public CaptureToken BeginCapture()
|
||||
{
|
||||
VsgDbgBeginCapture();
|
||||
|
||||
return new CaptureToken(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends a capture interval that was started with <see cref="BeginCapture"/>.
|
||||
/// </summary>
|
||||
public void EndCapture()
|
||||
{
|
||||
VsgDbgEndCapture();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the graphics log file, closes it, and frees resources that were used while the app was actively recording graphics information.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Ignored.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
VsgDbgUnInit();
|
||||
}
|
||||
|
||||
#region vsgcapture.h imports:
|
||||
|
||||
// extern "C" void __stdcall VsgDbgInit(_In_z_ wchar_t const * szVSGLog);
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgInit", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgInit(string logName);
|
||||
|
||||
// extern "C" void __stdcall VsgDbgUnInit();
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgUnInit", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgUnInit();
|
||||
|
||||
// extern "C" void __stdcall VsgDbgToggleHUD();
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgToggleHUD", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgToggleHUD();
|
||||
|
||||
// extern "C" void __stdcall VsgDbgCaptureCurrentFrame();
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgCaptureCurrentFrame", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgCaptureCurrentFrame();
|
||||
|
||||
// extern "C" void __stdcall VsgDbgBeginCapture();
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgBeginCapture", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgBeginCapture();
|
||||
|
||||
// extern "C" void __stdcall VsgDbgEndCapture();
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgEndCapture", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgEndCapture();
|
||||
|
||||
// extern "C" void __stdcall VsgDbgCopy(_In_z_ wchar_t const * szNewVSGLog);
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgCopy", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgCopy(string newLogName);
|
||||
|
||||
// extern "C" void __stdcall VsgDbgAddHUDMessage(_In_z_ wchar_t const * szMessage);
|
||||
[DllImport("VsGraphicsHelper.dll", EntryPoint = "VsgDbgAddHUDMessage", CharSet = CharSet.Unicode)]
|
||||
private static extern void VsgDbgAddHUDMessage(string message);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using SharpDX.Mathematics.Interop;
|
||||
|
||||
namespace SharpDX.Desktop.Direct3D
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for PIX.
|
||||
/// </summary>
|
||||
public static class PixHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the beginning of a user-defined event. PIX can use this event to trigger an action.
|
||||
/// </summary>
|
||||
/// <param name="color">The Event color.</param>
|
||||
/// <param name="name">The Event Name.</param>
|
||||
/// <returns>The zero-based level of the hierarchy that this event is starting in. If an error occurs, the return value will be negative.</returns>
|
||||
/// <unmanaged>D3DPERF_BeginEvent</unmanaged>
|
||||
public static int BeginEvent(RawColorBGRA color, string name)
|
||||
{
|
||||
return D3DPERF_BeginEvent(color, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the beginning of a user-defined event. PIX can use this event to trigger an action.
|
||||
/// </summary>
|
||||
/// <param name="color">The Event color.</param>
|
||||
/// <param name="name">The Event formatted Name.</param>
|
||||
/// <param name="parameters">The parameters to use for the formatted name.</param>
|
||||
/// <returns>
|
||||
/// The zero-based level of the hierarchy that this event is starting in. If an error occurs, the return value will be negative.
|
||||
/// </returns>
|
||||
/// <unmanaged>D3DPERF_BeginEvent</unmanaged>
|
||||
public static int BeginEvent(RawColorBGRA color, string name, params object[] parameters)
|
||||
{
|
||||
return D3DPERF_BeginEvent(color, string.Format(name, parameters));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the end of a user-defined event. PIX can use this event to trigger an action.
|
||||
/// </summary>
|
||||
/// <returns>The level of the hierarchy in which the event is ending. If an error occurs, this value is negative.</returns>
|
||||
/// <unmanaged>D3DPERF_EndEvent</unmanaged>
|
||||
public static int EndEvent()
|
||||
{
|
||||
return D3DPERF_EndEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark an instantaneous event. PIX can use this event to trigger an action.
|
||||
/// </summary>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <unmanaged>D3DPERF_SetMarker</unmanaged>
|
||||
public static void SetMarker(RawColorBGRA color, string name)
|
||||
{
|
||||
D3DPERF_SetMarker(color, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark an instantaneous event. PIX can use this event to trigger an action.
|
||||
/// </summary>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="name">The name to format.</param>
|
||||
/// <param name="parameters">The parameters to use to format the name.</param>
|
||||
/// <unmanaged>D3DPERF_SetMarker</unmanaged>
|
||||
public static void SetMarker(RawColorBGRA color, string name, params object[] parameters)
|
||||
{
|
||||
D3DPERF_SetMarker(color, string.Format(name, parameters));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set this to false to notify PIX that the target program does not give permission to be profiled.
|
||||
/// </summary>
|
||||
/// <param name="enableFlag">if set to <c>true</c> PIX profiling is authorized. Default to true.</param>
|
||||
/// <unmanaged>D3DPERF_SetOptions</unmanaged>
|
||||
public static void AllowProfiling(bool enableFlag)
|
||||
{
|
||||
D3DPERF_SetOptions(enableFlag ? 0 : 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is currently profiled by PIX.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is currently profiled; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
/// <unmanaged>D3DPERF_GetStatus</unmanaged>
|
||||
public static bool IsCurrentlyProfiled => D3DPERF_GetStatus() != 0;
|
||||
|
||||
[DllImport("d3d9.dll", EntryPoint = "D3DPERF_BeginEvent", CharSet = CharSet.Unicode)]
|
||||
private extern static int D3DPERF_BeginEvent(RawColorBGRA color, string name);
|
||||
|
||||
[DllImport("d3d9.dll", EntryPoint = "D3DPERF_EndEvent", CharSet = CharSet.Unicode)]
|
||||
private extern static int D3DPERF_EndEvent();
|
||||
|
||||
[DllImport("d3d9.dll", EntryPoint = "D3DPERF_SetMarker", CharSet = CharSet.Unicode)]
|
||||
private extern static void D3DPERF_SetMarker(RawColorBGRA color, string wszName);
|
||||
|
||||
[DllImport("d3d9.dll", EntryPoint = "D3DPERF_SetOptions", CharSet = CharSet.Unicode)]
|
||||
private extern static void D3DPERF_SetOptions( int options);
|
||||
|
||||
[DllImport("d3d9.dll", EntryPoint = "D3DPERF_GetStatus", CharSet = CharSet.Unicode)]
|
||||
private extern static int D3DPERF_GetStatus();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using SharpDX.Desktop.Collections;
|
||||
|
||||
namespace SharpDX.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a hook to WndProc of an existing window handle using <see cref="IMessageFilter"/>.
|
||||
/// </summary>
|
||||
public class MessageFilterHook
|
||||
{
|
||||
#region Constants and Fields
|
||||
|
||||
private static readonly Dictionary<IntPtr, MessageFilterHook> RegisteredHooks = new Dictionary<IntPtr, MessageFilterHook>(EqualityComparer.DefaultIntPtr);
|
||||
|
||||
private readonly IntPtr defaultWndProc;
|
||||
|
||||
private readonly IntPtr hwnd;
|
||||
|
||||
private readonly Win32Native.WndProc newWndProc;
|
||||
|
||||
private readonly IntPtr newWndProcPtr;
|
||||
|
||||
private List<IMessageFilter> currentFilters;
|
||||
|
||||
private bool isDisposed;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors and Destructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Win32.MessageFilterHook" /> class.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The HWND.</param>
|
||||
private MessageFilterHook(IntPtr hwnd)
|
||||
{
|
||||
this.currentFilters = new List<IMessageFilter>();
|
||||
this.hwnd = hwnd;
|
||||
|
||||
// Retrieve the previous WndProc associated with this window handle
|
||||
defaultWndProc = Win32Native.GetWindowLong(hwnd, Win32Native.WindowLongType.WndProc);
|
||||
|
||||
// Create a pointer to the new WndProc
|
||||
newWndProc = WndProc;
|
||||
newWndProcPtr = Marshal.GetFunctionPointerForDelegate(newWndProc);
|
||||
|
||||
// Set our own private wndproc in order to catch NCDestroy message
|
||||
Win32Native.SetWindowLong(hwnd, Win32Native.WindowLongType.WndProc, newWndProcPtr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods and Operators
|
||||
|
||||
/// <summary>
|
||||
/// Adds a message filter to a window.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle of the window.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
public static void AddMessageFilter(IntPtr hwnd, IMessageFilter messageFilter)
|
||||
{
|
||||
lock (RegisteredHooks)
|
||||
{
|
||||
hwnd = GetSafeWindowHandle(hwnd);
|
||||
if (!RegisteredHooks.TryGetValue(hwnd, out var hook))
|
||||
{
|
||||
hook = new MessageFilterHook(hwnd);
|
||||
RegisteredHooks.Add(hwnd, hook);
|
||||
}
|
||||
hook.AddMessageMilter(messageFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a message filter associated with a window.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle of the window.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
public static void RemoveMessageFilter(IntPtr hwnd, IMessageFilter messageFilter)
|
||||
{
|
||||
lock (RegisteredHooks)
|
||||
{
|
||||
hwnd = GetSafeWindowHandle(hwnd);
|
||||
if (RegisteredHooks.TryGetValue(hwnd, out var hook))
|
||||
{
|
||||
hook.RemoveMessageFilter(messageFilter);
|
||||
|
||||
if (hook.isDisposed)
|
||||
{
|
||||
RegisteredHooks.Remove(hwnd);
|
||||
hook.RestoreWndProc();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void AddMessageMilter(IMessageFilter filter)
|
||||
{
|
||||
// Make a copy of the filters in order to support a lightweight threadsafe
|
||||
var filters = new List<IMessageFilter>(this.currentFilters);
|
||||
if (!filters.Contains(filter))
|
||||
filters.Add(filter);
|
||||
this.currentFilters = filters;
|
||||
}
|
||||
|
||||
private void RemoveMessageFilter(IMessageFilter filter)
|
||||
{
|
||||
// Make a copy of the filters in order to support a lightweight threadsafe
|
||||
var filters = new List<IMessageFilter>(this.currentFilters);
|
||||
filters.Remove(filter);
|
||||
|
||||
// If there are no more filters, then we can remove the hook
|
||||
if (filters.Count == 0)
|
||||
{
|
||||
this.isDisposed = true;
|
||||
this.RestoreWndProc();
|
||||
}
|
||||
this.currentFilters = filters;
|
||||
}
|
||||
|
||||
private void RestoreWndProc()
|
||||
{
|
||||
var currentProc = Win32Native.GetWindowLong(hwnd, Win32Native.WindowLongType.WndProc);
|
||||
if (currentProc == newWndProcPtr)
|
||||
{
|
||||
// Restore back default WndProc only if the previous callback is owned by this message filter hook
|
||||
Win32Native.SetWindowLong(hwnd, Win32Native.WindowLongType.WndProc, defaultWndProc);
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
this.RestoreWndProc();
|
||||
else
|
||||
{
|
||||
var message = new Message() { HWnd = hwnd, LParam = lParam, Msg = msg, WParam = wParam };
|
||||
foreach (var messageFilter in this.currentFilters)
|
||||
{
|
||||
if (messageFilter.PreFilterMessage(ref message))
|
||||
return message.Result;
|
||||
}
|
||||
}
|
||||
|
||||
var result = Win32Native.CallWindowProc(defaultWndProc, hWnd, msg, wParam, lParam);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IntPtr GetSafeWindowHandle(IntPtr hwnd)
|
||||
{
|
||||
return hwnd == IntPtr.Zero ? Process.GetCurrentProcess().MainWindowHandle : hwnd;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace SharpDX.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// A Renderable UserControl.
|
||||
/// </summary>
|
||||
public class RenderControl : UserControl
|
||||
{
|
||||
private Font fontForDesignMode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderForm"/> class.
|
||||
/// </summary>
|
||||
public RenderControl()
|
||||
{
|
||||
SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque | ControlStyles.UserPaint, true );
|
||||
UpdateStyles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Paints the background of the control.
|
||||
/// </summary>
|
||||
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
if (DesignMode)
|
||||
base.OnPaintBackground(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
if (DesignMode)
|
||||
{
|
||||
if (fontForDesignMode == null)
|
||||
fontForDesignMode = new Font("Calibri", 24, FontStyle.Regular);
|
||||
|
||||
e.Graphics.Clear(System.Drawing.Color.WhiteSmoke);
|
||||
string text = "SharpDX RenderControl";
|
||||
var sizeText = e.Graphics.MeasureString(text, fontForDesignMode);
|
||||
|
||||
e.Graphics.DrawString(text, fontForDesignMode, new SolidBrush(System.Drawing.Color.Black), (Width - sizeText.Width) / 2, (Height - sizeText.Height) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// -----------------------------------------------------------------------------
|
||||
// Original code from SlimDX project.
|
||||
// Greetings to SlimDX Group. Original code published with the following license:
|
||||
// -----------------------------------------------------------------------------
|
||||
/*
|
||||
* Copyright (c) 2007-2011 SlimDX Group
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace SharpDX.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Default Rendering Form.
|
||||
/// </summary>
|
||||
public class RenderForm : Form
|
||||
{
|
||||
private const int WM_SIZE = 0x0005;
|
||||
private const int SIZE_RESTORED = 0;
|
||||
private const int SIZE_MINIMIZED = 1;
|
||||
private const int SIZE_MAXIMIZED = 2;
|
||||
private const int SIZE_MAXSHOW = 3;
|
||||
private const int SIZE_MAXHIDE = 4;
|
||||
private const int WM_ACTIVATEAPP = 0x001C;
|
||||
private const int WM_POWERBROADCAST = 0x0218;
|
||||
private const int WM_MENUCHAR = 0x0120;
|
||||
private const int WM_SYSCOMMAND = 0x0112;
|
||||
private const uint PBT_APMRESUMESUSPEND = 7;
|
||||
private const uint PBT_APMQUERYSUSPEND = 0;
|
||||
private const int SC_MONITORPOWER = 0xF170;
|
||||
private const int SC_SCREENSAVE = 0xF140;
|
||||
private const int WM_DISPLAYCHANGE = 0x007E;
|
||||
private const int MNC_CLOSE = 1;
|
||||
private System.Drawing.Size cachedSize;
|
||||
private FormWindowState previousWindowState;
|
||||
//private DisplayMonitor monitor;
|
||||
private bool isUserResizing;
|
||||
private bool allowUserResizing;
|
||||
private bool isBackgroundFirstDraw;
|
||||
private bool isSizeChangedWithoutResizeBegin;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderForm"/> class.
|
||||
/// </summary>
|
||||
public RenderForm() : this("SharpDX")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderForm"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
public RenderForm(String text)
|
||||
{
|
||||
Text = text;
|
||||
ClientSize = new System.Drawing.Size(800, 600);
|
||||
//MinimumSize = new System.Drawing.Size(200, 200);
|
||||
|
||||
ResizeRedraw = true;
|
||||
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
|
||||
|
||||
//Icon = Desktop.Properties.Resources.logo;
|
||||
|
||||
previousWindowState = FormWindowState.Normal;
|
||||
AllowUserResizing = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [app activated].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> AppActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [app deactivated].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> AppDeactivated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [monitor changed].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> MonitorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [pause rendering].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> PauseRendering;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [resume rendering].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> ResumeRendering;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [screensaver].
|
||||
/// </summary>
|
||||
public event EventHandler<CancelEventArgs> Screensaver;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [system resume].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> SystemResume;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [system suspend].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> SystemSuspend;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [user resized].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> UserResized;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this form can be resized by the user. See remarks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property alters <see cref="Form.FormBorderStyle"/>,
|
||||
/// for <c>true</c> value it is <see cref="FormBorderStyle.Sizable"/>,
|
||||
/// for <c>false</c> - <see cref="FormBorderStyle.FixedSingle"/>.
|
||||
/// </remarks>
|
||||
/// <value><c>true</c> if this form can be resized by the user (by default); otherwise, <c>false</c>.</value>
|
||||
public bool AllowUserResizing
|
||||
{
|
||||
get => allowUserResizing;
|
||||
set
|
||||
{
|
||||
if (allowUserResizing != value)
|
||||
{
|
||||
allowUserResizing = value;
|
||||
MaximizeBox = allowUserResizing;
|
||||
FormBorderStyle = IsFullscreen
|
||||
? FormBorderStyle.None
|
||||
: allowUserResizing ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicationg whether the current render form is in fullscreen mode. See remarks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If Toolkit is used, this property is set automatically,
|
||||
/// otherwise user should maintain it himself as it affects the behavior of <see cref="AllowUserResizing"/> property.
|
||||
/// </remarks>
|
||||
public bool IsFullscreen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:System.Windows.Forms.Form.ResizeBegin"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
||||
protected override void OnResizeBegin(EventArgs e)
|
||||
{
|
||||
isUserResizing = true;
|
||||
|
||||
base.OnResizeBegin(e);
|
||||
cachedSize = Size;
|
||||
OnPauseRendering(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:System.Windows.Forms.Form.ResizeEnd"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
||||
protected override void OnResizeEnd(EventArgs e)
|
||||
{
|
||||
base.OnResizeEnd(e);
|
||||
|
||||
if (isUserResizing && cachedSize != Size)
|
||||
{
|
||||
OnUserResized(e);
|
||||
// UpdateScreen();
|
||||
}
|
||||
|
||||
isUserResizing = false;
|
||||
OnResumeRendering(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:System.Windows.Forms.Form.Load"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
// UpdateScreen();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Paints the background of the control.
|
||||
/// </summary>
|
||||
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
if (!isBackgroundFirstDraw)
|
||||
{
|
||||
base.OnPaintBackground(e);
|
||||
isBackgroundFirstDraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Pause Rendering event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnPauseRendering(EventArgs e)
|
||||
{
|
||||
if (PauseRendering != null)
|
||||
PauseRendering(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Resume Rendering event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnResumeRendering(EventArgs e)
|
||||
{
|
||||
if (ResumeRendering != null)
|
||||
ResumeRendering(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the User resized event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnUserResized(EventArgs e)
|
||||
{
|
||||
if (UserResized != null)
|
||||
UserResized(this, e);
|
||||
}
|
||||
|
||||
private void OnMonitorChanged(EventArgs e)
|
||||
{
|
||||
if (MonitorChanged != null)
|
||||
MonitorChanged(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the On App Activated event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnAppActivated(EventArgs e)
|
||||
{
|
||||
if (AppActivated != null)
|
||||
AppActivated(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the App Deactivated event
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnAppDeactivated(EventArgs e)
|
||||
{
|
||||
if (AppDeactivated != null)
|
||||
AppDeactivated(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the System Suspend event
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnSystemSuspend(EventArgs e)
|
||||
{
|
||||
if (SystemSuspend != null)
|
||||
SystemSuspend(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the System Resume event
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
private void OnSystemResume(EventArgs e)
|
||||
{
|
||||
if (SystemResume != null)
|
||||
SystemResume(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:Screensaver"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
|
||||
private void OnScreensaver(CancelEventArgs e)
|
||||
{
|
||||
if (Screensaver != null)
|
||||
Screensaver(this, e);
|
||||
}
|
||||
|
||||
protected override void OnClientSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnClientSizeChanged(e);
|
||||
if (!isUserResizing && (isSizeChangedWithoutResizeBegin || cachedSize != Size))
|
||||
{
|
||||
isSizeChangedWithoutResizeBegin = false;
|
||||
cachedSize = Size;
|
||||
OnUserResized(EventArgs.Empty);
|
||||
//UpdateScreen();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override windows message loop handling.
|
||||
/// </summary>
|
||||
/// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
long wparam = m.WParam.ToInt64();
|
||||
|
||||
switch (m.Msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
if (wparam == SIZE_MINIMIZED)
|
||||
{
|
||||
previousWindowState = FormWindowState.Minimized;
|
||||
OnPauseRendering(EventArgs.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
Win32Native.GetClientRect(m.HWnd, out var rect);
|
||||
if (rect.Bottom - rect.Top == 0)
|
||||
{
|
||||
// Rapidly clicking the task bar to minimize and restore a window
|
||||
// can cause a WM_SIZE message with SIZE_RESTORED when
|
||||
// the window has actually become minimized due to rapid change
|
||||
// so just ignore this message
|
||||
}
|
||||
else if (wparam == SIZE_MAXIMIZED)
|
||||
{
|
||||
if (previousWindowState == FormWindowState.Minimized)
|
||||
OnResumeRendering(EventArgs.Empty);
|
||||
|
||||
previousWindowState = FormWindowState.Maximized;
|
||||
|
||||
OnUserResized(EventArgs.Empty);
|
||||
//UpdateScreen();
|
||||
cachedSize = Size;
|
||||
}
|
||||
else if (wparam == SIZE_RESTORED)
|
||||
{
|
||||
if (previousWindowState == FormWindowState.Minimized)
|
||||
OnResumeRendering(EventArgs.Empty);
|
||||
|
||||
if (!isUserResizing && (Size != cachedSize || previousWindowState == FormWindowState.Maximized))
|
||||
{
|
||||
previousWindowState = FormWindowState.Normal;
|
||||
|
||||
// Only update when cachedSize is != 0
|
||||
if (cachedSize != Size.Empty)
|
||||
{
|
||||
isSizeChangedWithoutResizeBegin = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
previousWindowState = FormWindowState.Normal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_ACTIVATEAPP:
|
||||
if (wparam != 0)
|
||||
OnAppActivated(EventArgs.Empty);
|
||||
else
|
||||
OnAppDeactivated(EventArgs.Empty);
|
||||
break;
|
||||
case WM_POWERBROADCAST:
|
||||
if (wparam == PBT_APMQUERYSUSPEND)
|
||||
{
|
||||
OnSystemSuspend(EventArgs.Empty);
|
||||
m.Result = new IntPtr(1);
|
||||
return;
|
||||
}
|
||||
else if (wparam == PBT_APMRESUMESUSPEND)
|
||||
{
|
||||
OnSystemResume(EventArgs.Empty);
|
||||
m.Result = new IntPtr(1);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case WM_MENUCHAR:
|
||||
m.Result = new IntPtr(MNC_CLOSE << 16); // IntPtr(MAKELRESULT(0, MNC_CLOSE));
|
||||
return;
|
||||
case WM_SYSCOMMAND:
|
||||
wparam &= 0xFFF0;
|
||||
if (wparam == SC_MONITORPOWER || wparam == SC_SCREENSAVE)
|
||||
{
|
||||
var e = new CancelEventArgs();
|
||||
OnScreensaver(e);
|
||||
if (e.Cancel)
|
||||
{
|
||||
m.Result = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_DISPLAYCHANGE:
|
||||
OnMonitorChanged(EventArgs.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
if (keyData == (Keys.Menu | Keys.Alt) || keyData == Keys.F10)
|
||||
return true;
|
||||
|
||||
return base.ProcessDialogKey(keyData);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace SharpDX.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// RenderLoop provides a rendering loop infrastructure. See remarks for usage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use static <see cref="Run(System.Windows.Forms.Control,SharpDX.Windows.RenderLoop.RenderCallback)"/>
|
||||
/// method to directly use a renderloop with a render callback or use your own loop:
|
||||
/// <code>
|
||||
/// control.Show();
|
||||
/// using (var loop = new RenderLoop(control))
|
||||
/// {
|
||||
/// while (loop.NextFrame())
|
||||
/// {
|
||||
/// // Perform draw operations here.
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// Note that the main control can be changed at anytime inside the loop.
|
||||
/// </remarks>
|
||||
public class RenderLoop : IDisposable
|
||||
{
|
||||
private IntPtr controlHandle;
|
||||
private Control control;
|
||||
private bool isControlAlive;
|
||||
private bool switchControl;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderLoop"/> class.
|
||||
/// </summary>
|
||||
public RenderLoop() {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderLoop"/> class.
|
||||
/// </summary>
|
||||
public RenderLoop(Control control)
|
||||
{
|
||||
Control = control;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the control to associate with the current render loop.
|
||||
/// </summary>
|
||||
/// <value>The control.</value>
|
||||
/// <exception cref="System.InvalidOperationException">Control is already disposed</exception>
|
||||
public Control Control
|
||||
{
|
||||
get => control;
|
||||
set
|
||||
{
|
||||
if(control == value) return;
|
||||
|
||||
// Remove any previous control
|
||||
if(control != null && !switchControl)
|
||||
{
|
||||
isControlAlive = false;
|
||||
control.Disposed -= ControlDisposed;
|
||||
controlHandle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (value != null && value.IsDisposed)
|
||||
{
|
||||
throw new InvalidOperationException("Control is already disposed");
|
||||
}
|
||||
|
||||
control = value;
|
||||
switchControl = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the render loop should use the default <see cref="Application.DoEvents"/> instead of a custom window message loop lightweight for GC. Default is false.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the render loop should use the default <see cref="Application.DoEvents"/> instead of a custom window message loop (default false); otherwise, <c>false</c>.</value>
|
||||
/// <remarks>By default, RenderLoop is using a custom window message loop that is more lightweight than <see cref="Application.DoEvents" /> to process windows event message.
|
||||
/// Set this parameter to true to use the default <see cref="Application.DoEvents"/>.</remarks>
|
||||
public bool UseApplicationDoEvents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calls this method on each frame.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if if the control is still active, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">An error occured </exception>
|
||||
public bool NextFrame()
|
||||
{
|
||||
// Setup new control
|
||||
// TODO this is not completely thread-safe. We should use a lock to handle this correctly
|
||||
if (switchControl && control != null)
|
||||
{
|
||||
controlHandle = control.Handle;
|
||||
control.Disposed += ControlDisposed;
|
||||
isControlAlive = true;
|
||||
switchControl = false;
|
||||
}
|
||||
|
||||
if(isControlAlive)
|
||||
{
|
||||
if(UseApplicationDoEvents)
|
||||
{
|
||||
// Revert back to Application.DoEvents in order to support Application.AddMessageFilter
|
||||
// Seems that DoEvents is compatible with Mono unlike Application.Run that was not running
|
||||
// correctly.
|
||||
Application.DoEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
var localHandle = controlHandle;
|
||||
if (localHandle != IntPtr.Zero)
|
||||
{
|
||||
// Previous code not compatible with Application.AddMessageFilter but faster then DoEvents
|
||||
while (Win32Native.PeekMessage(out var msg, IntPtr.Zero, 0, 0, 0) != 0)
|
||||
{
|
||||
if (Win32Native.GetMessage(out msg, IntPtr.Zero, 0, 0) == -1)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
|
||||
"An error happened in rendering loop while processing windows messages. Error: {0}",
|
||||
Marshal.GetLastWin32Error()));
|
||||
}
|
||||
|
||||
// NCDESTROY event?
|
||||
if (msg.msg == 130)
|
||||
{
|
||||
isControlAlive = false;
|
||||
}
|
||||
|
||||
var message = new Message() { HWnd = msg.handle, LParam = msg.lParam, Msg = (int)msg.msg, WParam = msg.wParam };
|
||||
if (!Application.FilterMessage(ref message))
|
||||
{
|
||||
Win32Native.TranslateMessage(ref msg);
|
||||
Win32Native.DispatchMessage(ref msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isControlAlive || switchControl;
|
||||
}
|
||||
|
||||
private void ControlDisposed(object sender, EventArgs e)
|
||||
{
|
||||
isControlAlive = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Control = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the rendering loop.
|
||||
/// </summary>
|
||||
public delegate void RenderCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Runs the specified main loop in the specified context.
|
||||
/// </summary>
|
||||
public static void Run(ApplicationContext context, RenderCallback renderCallback)
|
||||
{
|
||||
Run(context.MainForm, renderCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the specified main loop for the specified windows form.
|
||||
/// </summary>
|
||||
/// <param name="form">The form.</param>
|
||||
/// <param name="renderCallback">The rendering callback.</param>
|
||||
/// <param name="useApplicationDoEvents">if set to <c>true</c> indicating whether the render loop should use the default <see cref="Application.DoEvents"/> instead of a custom window message loop lightweight for GC. Default is false.</param>
|
||||
/// <exception cref="System.ArgumentNullException">form
|
||||
/// or
|
||||
/// renderCallback</exception>
|
||||
public static void Run(Control form, RenderCallback renderCallback, bool useApplicationDoEvents = false)
|
||||
{
|
||||
if(form == null) throw new ArgumentNullException("form");
|
||||
if(renderCallback == null) throw new ArgumentNullException("renderCallback");
|
||||
|
||||
form.Show();
|
||||
using (var renderLoop = new RenderLoop(form) { UseApplicationDoEvents = useApplicationDoEvents })
|
||||
{
|
||||
while(renderLoop.NextFrame())
|
||||
{
|
||||
renderCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is application idle.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is application idle; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public static bool IsIdle
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)(Win32Native.PeekMessage(out var msg, IntPtr.Zero, 0, 0, 0) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>TRACE;DIRECTX11_1;DIRECTX11_2;DirectX12;DESKTOP_APP</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>TRACE;DIRECTX11_1;DIRECTX11_2;DirectX12;DESKTOP_APP</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsDesktop.App" Version="3.0.0-alpha-27128-4" />
|
||||
<PackageReference Include="SharpDX" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using SharpDX.IO;
|
||||
using SharpDX.Mathematics.Interop;
|
||||
using SharpDX.Win32;
|
||||
|
||||
namespace SharpDX.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal class to interact with Native Message
|
||||
/// </summary>
|
||||
partial class Win32Native
|
||||
{
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "CreateFile", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr Create(
|
||||
string fileName,
|
||||
NativeFileAccess desiredAccess,
|
||||
NativeFileShare shareMode,
|
||||
IntPtr securityAttributes,
|
||||
NativeFileMode mode,
|
||||
NativeFileOptions flagsAndOptions,
|
||||
IntPtr templateFile);
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct TextMetric
|
||||
{
|
||||
public int tmHeight;
|
||||
public int tmAscent;
|
||||
public int tmDescent;
|
||||
public int tmInternalLeading;
|
||||
public int tmExternalLeading;
|
||||
public int tmAveCharWidth;
|
||||
public int tmMaxCharWidth;
|
||||
public int tmWeight;
|
||||
public int tmOverhang;
|
||||
public int tmDigitizedAspectX;
|
||||
public int tmDigitizedAspectY;
|
||||
public char tmFirstChar;
|
||||
public char tmLastChar;
|
||||
public char tmDefaultChar;
|
||||
public char tmBreakChar;
|
||||
public byte tmItalic;
|
||||
public byte tmUnderlined;
|
||||
public byte tmStruckOut;
|
||||
public byte tmPitchAndFamily;
|
||||
public byte tmCharSet;
|
||||
}
|
||||
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "PeekMessage")]
|
||||
public static extern int PeekMessage(out NativeMessage lpMsg, IntPtr hWnd, int wMsgFilterMin,
|
||||
int wMsgFilterMax, int wRemoveMsg);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetMessage")]
|
||||
public static extern int GetMessage(out NativeMessage lpMsg, IntPtr hWnd, int wMsgFilterMin,
|
||||
int wMsgFilterMax);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "TranslateMessage")]
|
||||
public static extern int TranslateMessage(ref NativeMessage lpMsg);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "DispatchMessage")]
|
||||
public static extern int DispatchMessage(ref NativeMessage lpMsg);
|
||||
|
||||
public enum WindowLongType : int
|
||||
{
|
||||
WndProc = (-4),
|
||||
HInstance = (-6),
|
||||
HwndParent = (-8),
|
||||
Style = (-16),
|
||||
ExtendedStyle = (-20),
|
||||
UserData = (-21),
|
||||
Id = (-12)
|
||||
}
|
||||
|
||||
public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
public static IntPtr GetWindowLong(IntPtr hWnd, WindowLongType index)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
return GetWindowLong32(hWnd, index);
|
||||
}
|
||||
return GetWindowLong64(hWnd, index);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetFocus", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr GetFocus();
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr GetWindowLong32(IntPtr hwnd, WindowLongType index);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr GetWindowLong64(IntPtr hwnd, WindowLongType index);
|
||||
|
||||
public static IntPtr SetWindowLong(IntPtr hwnd, WindowLongType index, IntPtr wndProcPtr)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
return SetWindowLong32(hwnd, index, wndProcPtr);
|
||||
}
|
||||
return SetWindowLongPtr64(hwnd, index, wndProcPtr);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetParent", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr SetWindowLong32(IntPtr hwnd, WindowLongType index, IntPtr wndProc);
|
||||
|
||||
|
||||
public static bool ShowWindow(IntPtr hWnd, bool windowVisible)
|
||||
{
|
||||
return ShowWindow(hWnd, windowVisible ? 1 : 0);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Unicode)]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int mCmdShow);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr SetWindowLongPtr64(IntPtr hwnd, WindowLongType index, IntPtr wndProc);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "CallWindowProc", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetClientRect")]
|
||||
public static extern bool GetClientRect(IntPtr hWnd, out RawRectangle lpRect);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandle", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
/**
|
||||
* Any rendering that utilizes a buffer from this class should synchronize rendering on the instance of this class
|
||||
* that is being used.
|
||||
*/
|
||||
public class BufferedCanvas : IDisposable
|
||||
{
|
||||
private readonly object _canvasLock = new object();
|
||||
|
||||
private volatile Bitmap _bgBuffer; // all drawing is done on this buffer.
|
||||
private volatile Bitmap _fgBuffer;
|
||||
private readonly Canvas _canvas = new Canvas();
|
||||
|
||||
/**
|
||||
* Call this method once drawing on a Canvas retrieved by {@link #getCanvas()} to mark
|
||||
* the buffer as fully rendered. Failure to call this method will result in nothing being drawn.
|
||||
*/
|
||||
public void Swap()
|
||||
{
|
||||
lock (_canvasLock)
|
||||
{
|
||||
var tmp = _bgBuffer;
|
||||
_bgBuffer = _fgBuffer;
|
||||
_fgBuffer = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(int w, int h)
|
||||
{
|
||||
lock (_canvasLock)
|
||||
{
|
||||
if (w <= 0 || h <= 0)
|
||||
{
|
||||
_bgBuffer = null;
|
||||
_fgBuffer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bgBuffer?.Dispose();
|
||||
_fgBuffer?.Dispose();
|
||||
|
||||
_bgBuffer = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888);
|
||||
_fgBuffer = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Canvas for drawing. Actual drawing should be synchronized on the instance
|
||||
* of BufferedCanvas being used.
|
||||
* @return The Canvas instance to draw onto. Returns null if drawing buffers have not
|
||||
* been initialized a la {@link #resize(int, int)}.
|
||||
*/
|
||||
public Canvas Canvas
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_canvasLock)
|
||||
{
|
||||
if (_bgBuffer != null)
|
||||
{
|
||||
_canvas.SetBitmap(_bgBuffer);
|
||||
return _canvas;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The most recent fully rendered Bitmsp
|
||||
*/
|
||||
public Bitmap Bitmap => _fgBuffer;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_bgBuffer != null)
|
||||
{
|
||||
_bgBuffer.Dispose();
|
||||
_bgBuffer = null;
|
||||
}
|
||||
|
||||
if (_fgBuffer != null)
|
||||
{
|
||||
_fgBuffer.Dispose();
|
||||
_fgBuffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class DirectRenderer : IGraphicsRenderer
|
||||
{
|
||||
private readonly NativeCanvas _canvas;
|
||||
private readonly ScalingCanvas _scalingCanvas;
|
||||
private IDrawable _drawable;
|
||||
private GraphicsView _graphicsView;
|
||||
private Color _backgroundColor;
|
||||
|
||||
public DirectRenderer(Context context)
|
||||
{
|
||||
_canvas = new NativeCanvas(context);
|
||||
_scalingCanvas = new ScalingCanvas(_canvas);
|
||||
}
|
||||
|
||||
public ICanvas Canvas => _scalingCanvas;
|
||||
|
||||
public IDrawable Drawable
|
||||
{
|
||||
get => _drawable;
|
||||
set
|
||||
{
|
||||
_drawable = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public GraphicsView GraphicsView
|
||||
{
|
||||
set => _graphicsView = value;
|
||||
}
|
||||
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => _backgroundColor;
|
||||
set => _backgroundColor = value;
|
||||
}
|
||||
|
||||
public void Draw(Canvas androidCanvas, RectangleF dirtyRect, bool inPanOrZoom)
|
||||
{
|
||||
_canvas.Canvas = androidCanvas;
|
||||
|
||||
try
|
||||
{
|
||||
if (_backgroundColor != null)
|
||||
{
|
||||
_canvas.FillColor = _backgroundColor;
|
||||
_canvas.FillRectangle(dirtyRect);
|
||||
_canvas.FillColor = Colors.White;
|
||||
}
|
||||
|
||||
_drawable.Draw(_scalingCanvas, dirtyRect);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Error("An unexpected error occurred rendering the drawing.", exc);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_canvas.Canvas = null;
|
||||
_scalingCanvas.ResetState();
|
||||
}
|
||||
}
|
||||
|
||||
public void SizeChanged(int width, int height)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void Detached()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
_graphicsView?.Invalidate();
|
||||
}
|
||||
|
||||
public void Invalidate(float x, float y, float w, float h)
|
||||
{
|
||||
_graphicsView?.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
using System.Text;
|
||||
using Java.IO;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
// The class which loads the TTF file, parses it and returns the TTF font name
|
||||
public class FontAnalyzer
|
||||
{
|
||||
// Font file; must be seekable
|
||||
private RandomAccessFile _mFile;
|
||||
|
||||
// This function parses the TTF file and returns the font name specified in the file
|
||||
public FontInfo GetFontInfo(string aFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parses the TTF file format.
|
||||
// See http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html
|
||||
_mFile = new RandomAccessFile(aFileName, "r");
|
||||
|
||||
// Read the version first
|
||||
int version = ReadDWord();
|
||||
|
||||
// The version must be either 'true' (0x74727565) or 0x00010000 or 'OTTO' (0x4f54544f) for CFF style fonts.
|
||||
if (version != 0x74727565 && version != 0x00010000 && version != 0x4f54544f)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The TTF file consist of several sections called "tables", and we need to know how many of them are there.
|
||||
int numTables = ReadWord();
|
||||
|
||||
// Skip the rest in the header
|
||||
ReadWord(); // skip searchRange
|
||||
ReadWord(); // skip entrySelector
|
||||
ReadWord(); // skip rangeShift
|
||||
|
||||
// Now we can read the tables
|
||||
for (int i = 0; i < numTables; i++)
|
||||
{
|
||||
// Read the table entry
|
||||
int tag = ReadDWord();
|
||||
ReadDWord(); // skip checksum
|
||||
int offset = ReadDWord();
|
||||
int length = ReadDWord();
|
||||
|
||||
// Now here' the trick. 'name' field actually contains the textual string name.
|
||||
// So the 'name' string in characters equals to 0x6E616D65
|
||||
if (tag == 0x6E616D65)
|
||||
{
|
||||
// Here's the name section. Read it completely into the allocated buffer
|
||||
var table = new byte[length];
|
||||
|
||||
_mFile.Seek(offset);
|
||||
Read(table);
|
||||
|
||||
// This is also a table. See http://developer.apple.com/fonts/ttrefman/rm06/Chap6name.html
|
||||
// According to Table 36, the total number of table records is stored in the second word, at the offset 2.
|
||||
// Getting the count and string offset - remembering it's big endian.
|
||||
int count = GetWord(table, 2);
|
||||
int stringOffset = GetWord(table, 4);
|
||||
|
||||
string familyName = null;
|
||||
string styleName = null;
|
||||
string fullName = null;
|
||||
|
||||
// Record starts from offset 6
|
||||
for (int record = 0; record < count; record++)
|
||||
{
|
||||
// Table 37 tells us that each record is 6 words -> 12 bytes, and that the nameID is 4th word so its offset is 6.
|
||||
// We also need to account for the first 6 bytes of the header above (Table 36), so...
|
||||
int nameidOffset = record * 12 + 6;
|
||||
int platformId = GetWord(table, nameidOffset);
|
||||
int nameidValue = GetWord(table, nameidOffset + 6);
|
||||
|
||||
if (nameidValue == 1 && platformId == 1)
|
||||
familyName = ReadTableEntry(table, nameidOffset, stringOffset);
|
||||
|
||||
if (nameidValue == 2 && platformId == 1)
|
||||
styleName = ReadTableEntry(table, nameidOffset, stringOffset);
|
||||
|
||||
if (nameidValue == 4 && platformId == 1)
|
||||
fullName = ReadTableEntry(table, nameidOffset, stringOffset);
|
||||
|
||||
if (familyName != null && styleName != null && fullName != null)
|
||||
return new FontInfo(aFileName, familyName, styleName, fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Info(e);
|
||||
// Most likely a corrupted font file
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadTableEntry(byte[] table, int nameidOffset, int stringOffset)
|
||||
{
|
||||
// We need the string offset and length, which are the word 6 and 5 respectively
|
||||
int nameLength = GetWord(table, nameidOffset + 8);
|
||||
int nameOffset = GetWord(table, nameidOffset + 10);
|
||||
|
||||
// The real name string offset is calculated by adding the string_offset
|
||||
nameOffset = nameOffset + stringOffset;
|
||||
|
||||
// Make sure it is inside the array
|
||||
if (nameOffset >= 0 && nameOffset + nameLength < table.Length)
|
||||
return Encoding.UTF8.GetString(table, nameOffset, nameLength);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper I/O functions
|
||||
private int ReadByte()
|
||||
{
|
||||
return _mFile.Read() & 0xFF;
|
||||
}
|
||||
|
||||
private int ReadWord()
|
||||
{
|
||||
int b1 = ReadByte();
|
||||
int b2 = ReadByte();
|
||||
|
||||
return b1 << 8 | b2;
|
||||
}
|
||||
|
||||
private int ReadDWord()
|
||||
{
|
||||
int b1 = ReadByte();
|
||||
int b2 = ReadByte();
|
||||
int b3 = ReadByte();
|
||||
int b4 = ReadByte();
|
||||
|
||||
return b1 << 24 | b2 << 16 | b3 << 8 | b4;
|
||||
}
|
||||
|
||||
private void Read(byte[] array)
|
||||
{
|
||||
if (_mFile.Read(array) != array.Length)
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
// Helper
|
||||
private int GetWord(byte[] array, int offset)
|
||||
{
|
||||
int b1 = array[offset] & 0xFF;
|
||||
int b2 = array[offset + 1] & 0xFF;
|
||||
|
||||
return b1 << 8 | b2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace System.Graphics.Android
|
||||
{
|
||||
public class FontInfo
|
||||
{
|
||||
public FontInfo(string path, string family, string style, string fullName)
|
||||
{
|
||||
Path = path;
|
||||
Family = family;
|
||||
Style = style;
|
||||
FullName = fullName;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
public string Family { get; }
|
||||
public string Style { get; }
|
||||
public string FullName { get; }
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
src/libraries/System.Graphics.Android/Fonts/TeXGyreCursor-BoldItalic.otf
Normal file
Двоичные данные
src/libraries/System.Graphics.Android/Fonts/TeXGyreCursor-BoldItalic.otf
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
src/libraries/System.Graphics.Android/Fonts/TeXGyreTermes-BoldItalic.otf
Normal file
Двоичные данные
src/libraries/System.Graphics.Android/Fonts/TeXGyreTermes-BoldItalic.otf
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,501 @@
|
|||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public static class GraphicsExtensions
|
||||
{
|
||||
public static global::Android.Graphics.Color AsColorMultiplyAlpha(this Color target, float alpha)
|
||||
{
|
||||
var r = (int) (target.Red * 255f);
|
||||
var g = (int) (target.Green * 255f);
|
||||
var b = (int) (target.Blue * 255f);
|
||||
var a = (int) (target.Alpha * alpha * 255f);
|
||||
|
||||
if (a > 255)
|
||||
{
|
||||
a = 255;
|
||||
}
|
||||
|
||||
var color = new global::Android.Graphics.Color(r, g, b, a);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static int ToArgb(this Color target)
|
||||
{
|
||||
var a = (int) (target.Alpha * 255f);
|
||||
var r = (int) (target.Red * 255f);
|
||||
var g = (int) (target.Green * 255f);
|
||||
var b = (int) (target.Blue * 255f);
|
||||
|
||||
int argb = a << 24 | r << 16 | g << 8 | b;
|
||||
return argb;
|
||||
}
|
||||
|
||||
public static int ToArgb(this Color target, float alpha)
|
||||
{
|
||||
var a = (int) (target.Alpha * 255f * alpha);
|
||||
var r = (int) (target.Red * 255f);
|
||||
var g = (int) (target.Green * 255f);
|
||||
var b = (int) (target.Blue * 255f);
|
||||
|
||||
int argb = a << 24 | r << 16 | g << 8 | b;
|
||||
return argb;
|
||||
}
|
||||
|
||||
public static global::Android.Graphics.Color AsColor(this Color target)
|
||||
{
|
||||
var r = (int) (target.Red * 255f);
|
||||
var g = (int) (target.Green * 255f);
|
||||
var b = (int) (target.Blue * 255f);
|
||||
var a = (int) (target.Alpha * 255f);
|
||||
return new global::Android.Graphics.Color(r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color AsColor(this global::Android.Graphics.Color target)
|
||||
{
|
||||
var r = (int) target.R;
|
||||
var g = (int) target.G;
|
||||
var b = (int) target.B;
|
||||
var a = (int) target.A;
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
public static Drawing.RectangleF AsRectangleF(this RectangleF target)
|
||||
{
|
||||
return new Drawing.RectangleF(target.Left, target.Top, Math.Abs(target.Width), Math.Abs(target.Height));
|
||||
}
|
||||
|
||||
public static RectF AsRectF(this RectangleF target)
|
||||
{
|
||||
return new RectF(target.Left, target.Top, target.Right, target.Bottom);
|
||||
}
|
||||
|
||||
public static RectangleF AsRectangleF(this Drawing.RectangleF target)
|
||||
{
|
||||
return new RectangleF(target.Left, target.Top, Math.Abs(target.Width), Math.Abs(target.Height));
|
||||
}
|
||||
|
||||
public static RectangleF AsRectangleF(this RectF target)
|
||||
{
|
||||
return new RectangleF(target.Left, target.Top, Math.Abs(target.Width()), Math.Abs(target.Height()));
|
||||
}
|
||||
|
||||
public static RectF AsRectF(this Rect target)
|
||||
{
|
||||
return new RectF(target);
|
||||
}
|
||||
|
||||
public static global::Android.Graphics.PointF ToPointF(this PointF target)
|
||||
{
|
||||
return new global::Android.Graphics.PointF(target.X, target.Y);
|
||||
}
|
||||
|
||||
/* public static EWPath AsEWPath(this global::Android.Graphics.Path target)
|
||||
{
|
||||
var vPath = new EWPath();
|
||||
|
||||
var vConverter = new PathConverter(vPath);
|
||||
target.Apply(vConverter.ApplyCGPathFunction);
|
||||
|
||||
return vPath;
|
||||
} */
|
||||
|
||||
public static Matrix AsMatrix(this AffineTransform transform)
|
||||
{
|
||||
var values = new float[9];
|
||||
transform.GetMatrix(values);
|
||||
|
||||
values[Matrix.Mpersp0] = 0; // 6
|
||||
values[Matrix.Mpersp1] = 0; // 7
|
||||
values[Matrix.Mpersp2] = 1; // 8
|
||||
|
||||
var matrix = new Matrix();
|
||||
matrix.SetValues(values);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Path AsAndroidPath(
|
||||
this PathF path,
|
||||
float offsetX = 0,
|
||||
float offsetY = 0,
|
||||
float scaleX = 1,
|
||||
float scaleY = 1)
|
||||
{
|
||||
var nativePath = new Path();
|
||||
|
||||
int pointIndex = 0;
|
||||
int arcAngleIndex = 0;
|
||||
int arcClockwiseIndex = 0;
|
||||
|
||||
foreach (PathOperation vType in path.SegmentTypes)
|
||||
{
|
||||
if (vType == PathOperation.Move)
|
||||
{
|
||||
var point = path[pointIndex++];
|
||||
nativePath.MoveTo(offsetX + point.X * scaleX, offsetY + point.Y * scaleY);
|
||||
}
|
||||
else if (vType == PathOperation.Line)
|
||||
{
|
||||
var point = path[pointIndex++];
|
||||
nativePath.LineTo(offsetX + point.X * scaleX, offsetY + point.Y * scaleY);
|
||||
}
|
||||
|
||||
else if (vType == PathOperation.Quad)
|
||||
{
|
||||
var controlPoint = path[pointIndex++];
|
||||
var point = path[pointIndex++];
|
||||
nativePath.QuadTo(offsetX + controlPoint.X * scaleX, offsetY + controlPoint.Y * scaleY, offsetX + point.X * scaleX, offsetY + point.Y * scaleY);
|
||||
}
|
||||
else if (vType == PathOperation.Cubic)
|
||||
{
|
||||
var controlPoint1 = path[pointIndex++];
|
||||
var controlPoint2 = path[pointIndex++];
|
||||
var point = path[pointIndex++];
|
||||
nativePath.CubicTo(offsetX + controlPoint1.X * scaleX, offsetY + controlPoint1.Y * scaleY, offsetX + controlPoint2.X * scaleX, offsetY + controlPoint2.Y * scaleY, offsetX + point.X * scaleX,
|
||||
offsetY + point.Y * scaleY);
|
||||
}
|
||||
else if (vType == PathOperation.Arc)
|
||||
{
|
||||
var topLeft = path[pointIndex++];
|
||||
var bottomRight = path[pointIndex++];
|
||||
var startAngle = path.GetArcAngle(arcAngleIndex++);
|
||||
var endAngle = path.GetArcAngle(arcAngleIndex++);
|
||||
var clockwise = path.GetArcClockwise(arcClockwiseIndex++);
|
||||
|
||||
while (startAngle < 0)
|
||||
{
|
||||
startAngle += 360;
|
||||
}
|
||||
|
||||
while (endAngle < 0)
|
||||
{
|
||||
endAngle += 360;
|
||||
}
|
||||
|
||||
var rect = new RectF(offsetX + topLeft.X * scaleX, offsetY + topLeft.Y * scaleY, offsetX + bottomRight.X * scaleX, offsetY + bottomRight.Y * scaleY);
|
||||
var sweep = Geometry.GetSweep(startAngle, endAngle, clockwise);
|
||||
|
||||
startAngle *= -1;
|
||||
if (!clockwise)
|
||||
{
|
||||
sweep *= -1;
|
||||
}
|
||||
|
||||
nativePath.ArcTo(rect, startAngle, sweep);
|
||||
}
|
||||
else if (vType == PathOperation.Close)
|
||||
{
|
||||
nativePath.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return nativePath;
|
||||
}
|
||||
|
||||
public static Path AsAndroidPath(this PathF path, float ppu, float zoom)
|
||||
{
|
||||
return AsAndroidPath(path, ppu * zoom);
|
||||
}
|
||||
|
||||
public static Path AsAndroidPathFromSegment(this PathF target, int segmentIndex, float ppu, float zoom)
|
||||
{
|
||||
ppu = zoom * ppu;
|
||||
|
||||
var path = new Path();
|
||||
|
||||
var type = target.GetSegmentType(segmentIndex);
|
||||
if (type == PathOperation.Line)
|
||||
{
|
||||
int pointIndex = target.GetSegmentPointIndex(segmentIndex);
|
||||
var startPoint = target[pointIndex - 1];
|
||||
path.MoveTo(startPoint.X * ppu, startPoint.Y * ppu);
|
||||
|
||||
var endPoint = target[pointIndex];
|
||||
path.LineTo(endPoint.X * ppu, endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Quad)
|
||||
{
|
||||
int pointIndex = target.GetSegmentPointIndex(segmentIndex);
|
||||
var startPoint = target[pointIndex - 1];
|
||||
path.MoveTo(startPoint.X * ppu, startPoint.Y * ppu);
|
||||
|
||||
var controlPoint = target[pointIndex++];
|
||||
var endPoint = target[pointIndex];
|
||||
path.QuadTo(controlPoint.X * ppu, controlPoint.Y * ppu, endPoint.X * ppu, endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Cubic)
|
||||
{
|
||||
int pointIndex = target.GetSegmentPointIndex(segmentIndex);
|
||||
var startPoint = target[pointIndex - 1];
|
||||
path.MoveTo(startPoint.X * ppu, startPoint.Y * ppu);
|
||||
|
||||
var controlPoint1 = target[pointIndex++];
|
||||
var controlPoint2 = target[pointIndex++];
|
||||
var endPoint = target[pointIndex];
|
||||
path.CubicTo(controlPoint1.X * ppu, controlPoint1.Y * ppu, controlPoint2.X * ppu, controlPoint2.Y * ppu, endPoint.X * ppu, endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Arc)
|
||||
{
|
||||
target.GetSegmentInfo(segmentIndex, out var pointIndex, out var arcAngleIndex, out var arcClockwiseIndex);
|
||||
|
||||
var topLeft = target[pointIndex++];
|
||||
var bottomRight = target[pointIndex++];
|
||||
var startAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
var endAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
var clockwise = target.GetArcClockwise(arcClockwiseIndex++);
|
||||
|
||||
while (startAngle < 0)
|
||||
{
|
||||
startAngle += 360;
|
||||
}
|
||||
|
||||
while (endAngle < 0)
|
||||
{
|
||||
endAngle += 360;
|
||||
}
|
||||
|
||||
var rect = new RectF(topLeft.X * ppu, topLeft.Y * ppu, bottomRight.X * ppu, bottomRight.Y * ppu);
|
||||
var sweep = Geometry.GetSweep(startAngle, endAngle, clockwise);
|
||||
|
||||
startAngle *= -1;
|
||||
if (!clockwise)
|
||||
{
|
||||
sweep *= -1;
|
||||
}
|
||||
|
||||
path.ArcTo(rect, startAngle, sweep);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static Path AsRotatedAndroidPath(this PathF target, PointF center, float ppu, float zoom, float angle)
|
||||
{
|
||||
ppu = zoom * ppu;
|
||||
|
||||
var path = new Path();
|
||||
|
||||
int pointIndex = 0;
|
||||
int arcAngleIndex = 0;
|
||||
int arcClockwiseIndex = 0;
|
||||
|
||||
foreach (var type in target.SegmentTypes)
|
||||
{
|
||||
if (type == PathOperation.Move)
|
||||
{
|
||||
var point = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
path.MoveTo(point.X * ppu, point.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Line)
|
||||
{
|
||||
var endPoint = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
path.LineTo(endPoint.X * ppu, endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Quad)
|
||||
{
|
||||
var controlPoint1 = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
var endPoint = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
path.QuadTo(
|
||||
controlPoint1.X * ppu,
|
||||
controlPoint1.Y * ppu,
|
||||
endPoint.X * ppu,
|
||||
endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Cubic)
|
||||
{
|
||||
var controlPoint1 = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
var controlPoint2 = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
var endPoint = target.GetRotatedPoint(pointIndex++, center, angle);
|
||||
path.CubicTo(
|
||||
controlPoint1.X * ppu,
|
||||
controlPoint1.Y * ppu,
|
||||
controlPoint2.X * ppu,
|
||||
controlPoint2.Y * ppu,
|
||||
endPoint.X * ppu,
|
||||
endPoint.Y * ppu);
|
||||
}
|
||||
else if (type == PathOperation.Arc)
|
||||
{
|
||||
var topLeft = target[pointIndex++];
|
||||
var bottomRight = target[pointIndex++];
|
||||
var startAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
var endAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
var clockwise = target.GetArcClockwise(arcClockwiseIndex++);
|
||||
|
||||
while (startAngle < 0)
|
||||
{
|
||||
startAngle += 360;
|
||||
}
|
||||
|
||||
while (endAngle < 0)
|
||||
{
|
||||
endAngle += 360;
|
||||
}
|
||||
|
||||
var rect = new RectF(topLeft.X * ppu, topLeft.Y * ppu, bottomRight.X * ppu, bottomRight.Y * ppu);
|
||||
var sweep = Geometry.GetSweep(startAngle, endAngle, clockwise);
|
||||
|
||||
startAngle *= -1;
|
||||
if (!clockwise)
|
||||
{
|
||||
sweep *= -1;
|
||||
}
|
||||
|
||||
path.ArcTo(rect, startAngle, sweep);
|
||||
}
|
||||
else if (type == PathOperation.Close)
|
||||
{
|
||||
path.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static SizeF AsSizeF(this Drawing.SizeF target)
|
||||
{
|
||||
return new SizeF(target.Width, target.Height);
|
||||
}
|
||||
|
||||
public static Drawing.SizeF AsSizeF(this SizeF target)
|
||||
{
|
||||
return new Drawing.SizeF(target.Width, target.Height);
|
||||
}
|
||||
|
||||
public static PointF AsPointF(this global::Android.Graphics.PointF target)
|
||||
{
|
||||
return new PointF(target.X, target.Y);
|
||||
}
|
||||
|
||||
public static Bitmap GetPatternBitmap(this Paint paint, float scale = 1)
|
||||
{
|
||||
var pattern = paint?.Pattern;
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
using (var context = new NativeBitmapExportContext((int) (pattern.Width * scale), (int) (pattern.Height * scale), scale, disposeBitmap: false))
|
||||
{
|
||||
var canvas = context.Canvas;
|
||||
canvas.Scale(scale, scale);
|
||||
pattern.Draw(canvas);
|
||||
return context.Bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetPatternBitmap(
|
||||
this Paint paint,
|
||||
float scaleX,
|
||||
float scaleY)
|
||||
{
|
||||
var pattern = paint?.Pattern;
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
using (var context = new NativeBitmapExportContext((int) (pattern.Width * scaleX), (int) (pattern.Height * scaleY), disposeBitmap: false))
|
||||
{
|
||||
var scalingCanvas = new ScalingCanvas(context.Canvas);
|
||||
scalingCanvas.Scale(scaleX, scaleY);
|
||||
|
||||
pattern.Draw(scalingCanvas);
|
||||
|
||||
return context.Bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
public static SizeF GetTextSizeAsEWSize(this StaticLayout target, bool hasBoundedWidth)
|
||||
{
|
||||
// We need to know if the static layout was created with a bounded width, as this is what
|
||||
// StaticLayout.Width returns.
|
||||
if (hasBoundedWidth)
|
||||
return new SizeF(target.Width, target.Height);
|
||||
|
||||
float maxWidth = 0;
|
||||
int lineCount = target.LineCount;
|
||||
|
||||
for (int i = 0; i < lineCount; i++)
|
||||
{
|
||||
float lineWidth = target.GetLineWidth(i);
|
||||
if (lineWidth > maxWidth)
|
||||
{
|
||||
maxWidth = lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return new SizeF(maxWidth, target.Height);
|
||||
}
|
||||
|
||||
public static Drawing.SizeF GetOffsetsToDrawText(
|
||||
this StaticLayout target,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
HorizontalAlignment horizontalAlignment,
|
||||
VerticalAlignment verticalAlignment)
|
||||
{
|
||||
if (verticalAlignment != VerticalAlignment.Top)
|
||||
{
|
||||
Drawing.SizeF vTextFrameSize = target.GetTextSize();
|
||||
|
||||
float vOffsetX = 0;
|
||||
float vOffsetY = 0;
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
if (verticalAlignment == VerticalAlignment.Bottom)
|
||||
vOffsetY = height - vTextFrameSize.Height;
|
||||
else
|
||||
vOffsetY = (height - vTextFrameSize.Height) / 2;
|
||||
}
|
||||
|
||||
return new Drawing.SizeF(x + vOffsetX, y + vOffsetY);
|
||||
}
|
||||
|
||||
return new Drawing.SizeF(x, y);
|
||||
}
|
||||
|
||||
public static Bitmap Downsize(this Bitmap target, int maxSize, bool dispose = true)
|
||||
{
|
||||
if (target.Width > maxSize || target.Height > maxSize)
|
||||
{
|
||||
float factor;
|
||||
|
||||
if (target.Width > target.Height)
|
||||
{
|
||||
factor = maxSize / (float) target.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
factor = maxSize / (float) target.Height;
|
||||
}
|
||||
|
||||
var w = (int) Math.Round(factor * target.Width);
|
||||
var h = (int) Math.Round(factor * target.Height);
|
||||
|
||||
var newImage = Bitmap.CreateScaledBitmap(target, w, h, true);
|
||||
if (dispose)
|
||||
{
|
||||
target.Recycle();
|
||||
target.Dispose();
|
||||
}
|
||||
|
||||
return newImage;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
public static Bitmap Downsize(this Bitmap target, int maxWidth, int maxHeight, bool dispose = true)
|
||||
{
|
||||
var newImage = Bitmap.CreateScaledBitmap(target, maxWidth, maxHeight, true);
|
||||
if (dispose)
|
||||
{
|
||||
target.Recycle();
|
||||
target.Dispose();
|
||||
}
|
||||
|
||||
return newImage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class GraphicsView : View
|
||||
{
|
||||
private RectangleF _dirtyRect;
|
||||
private IGraphicsRenderer _renderer;
|
||||
private IDrawable _drawable;
|
||||
private int _width, _height;
|
||||
private bool _inPanOrZoom;
|
||||
|
||||
public GraphicsView(Context context, IAttributeSet attrs, IDrawable drawable = null, IGraphicsRenderer renderer = null) : base(context, attrs)
|
||||
{
|
||||
Drawable = drawable;
|
||||
Renderer = renderer;
|
||||
}
|
||||
|
||||
public GraphicsView(Context context, IDrawable drawable = null, IGraphicsRenderer renderer = null) : base(context)
|
||||
{
|
||||
Drawable = drawable;
|
||||
Renderer = renderer;
|
||||
}
|
||||
|
||||
public bool InPanOrZoom
|
||||
{
|
||||
get => _inPanOrZoom;
|
||||
set => _inPanOrZoom = value;
|
||||
}
|
||||
|
||||
public IGraphicsRenderer Renderer
|
||||
{
|
||||
get => _renderer;
|
||||
|
||||
set
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.Drawable = null;
|
||||
_renderer.GraphicsView = null;
|
||||
_renderer.Dispose();
|
||||
}
|
||||
|
||||
_renderer = value ?? new DirectRenderer(Context);
|
||||
|
||||
_renderer.GraphicsView = this;
|
||||
_renderer.Drawable = _drawable;
|
||||
_renderer.SizeChanged(_width, _height);
|
||||
}
|
||||
}
|
||||
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => _renderer.BackgroundColor;
|
||||
set => _renderer.BackgroundColor = value;
|
||||
}
|
||||
|
||||
public IDrawable Drawable
|
||||
{
|
||||
get => _drawable;
|
||||
set
|
||||
{
|
||||
_drawable = value;
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.Drawable = _drawable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Canvas androidCanvas)
|
||||
{
|
||||
if (_drawable == null) return;
|
||||
|
||||
_dirtyRect.Width = Width;
|
||||
_dirtyRect.Height = Height;
|
||||
_renderer.Draw(androidCanvas, _dirtyRect, _inPanOrZoom);
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged(int width, int height, int oldWidth, int oldHeight)
|
||||
{
|
||||
base.OnSizeChanged(width, height, oldWidth, oldHeight);
|
||||
_renderer.SizeChanged(width, height);
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromWindow()
|
||||
{
|
||||
_renderer.Detached();
|
||||
}
|
||||
|
||||
public void InvalidateDrawable()
|
||||
{
|
||||
_renderer.Invalidate();
|
||||
}
|
||||
|
||||
public void InvalidateDrawable(float x, float y, float w, float h)
|
||||
{
|
||||
_renderer.Invalidate(x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public interface IGraphicsRenderer : IDisposable
|
||||
{
|
||||
GraphicsView GraphicsView { set; }
|
||||
ICanvas Canvas { get; }
|
||||
IDrawable Drawable { get; set; }
|
||||
Color BackgroundColor { get; set; }
|
||||
void Draw(Canvas androidCanvas, RectangleF dirtyRect, bool inPanOrZoom);
|
||||
void SizeChanged(int width, int height);
|
||||
void Detached();
|
||||
void Invalidate();
|
||||
void Invalidate(float x, float y, float w, float h);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public static class ImageExtensions
|
||||
{
|
||||
public static Bitmap AsBitmap(this IImage image)
|
||||
{
|
||||
if (image is NativeImage mdimage)
|
||||
{
|
||||
return mdimage.NativeRepresentation;
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
Logger.Warn("MDImageExtensions.AsBitmap: Unable to get Bitmap from EWImage. Expected an image of type MDImage however an image of type {0} was received.", image.GetType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System.IO;
|
||||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeBitmapExportContext : BitmapExportContext
|
||||
{
|
||||
private Bitmap _bitmap;
|
||||
private Canvas _androidCanvas;
|
||||
private readonly ScalingCanvas _canvas;
|
||||
private readonly bool _disposeBitmap;
|
||||
|
||||
public NativeBitmapExportContext(int width, int height, float displayScale = 1, int dpi = 72, bool disposeBitmap = true, bool transparent = true) : base(width, height, dpi)
|
||||
{
|
||||
_bitmap = Bitmap.CreateBitmap(
|
||||
width,
|
||||
height,
|
||||
transparent
|
||||
? Bitmap.Config.Argb8888
|
||||
: Bitmap.Config.Rgb565);
|
||||
|
||||
_androidCanvas = new Canvas(_bitmap);
|
||||
var nativeCanvas = new NativeCanvas(null)
|
||||
{
|
||||
Canvas = _androidCanvas,
|
||||
};
|
||||
_canvas = new ScalingCanvas(nativeCanvas)
|
||||
{
|
||||
DisplayScale = displayScale
|
||||
};
|
||||
_disposeBitmap = disposeBitmap;
|
||||
}
|
||||
|
||||
public override ICanvas Canvas => _canvas;
|
||||
|
||||
public override IImage Image => new NativeImage(Bitmap);
|
||||
|
||||
public Bitmap Bitmap => _bitmap;
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_androidCanvas != null)
|
||||
{
|
||||
_androidCanvas.Dispose();
|
||||
_androidCanvas = null;
|
||||
}
|
||||
|
||||
if (_bitmap != null && _disposeBitmap)
|
||||
{
|
||||
_bitmap.Dispose();
|
||||
_bitmap = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public override void WriteToStream(Stream stream)
|
||||
{
|
||||
_bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,816 @@
|
|||
using System.Graphics.Android.Text;
|
||||
using System.Graphics.Text;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeCanvas : AbstractCanvas<NativeCanvasState>
|
||||
{
|
||||
private static global::Android.Graphics.Paint _defaultFillPaint;
|
||||
private static TextPaint _defaultFontPaint;
|
||||
private static global::Android.Graphics.Paint _defaultStrokePaint;
|
||||
|
||||
private Canvas _canvas;
|
||||
private Shader _shader;
|
||||
|
||||
private readonly Matrix _shaderMatrix = new Matrix();
|
||||
|
||||
public NativeCanvas(Context context) : base(CreateNewState, CreateStateCopy)
|
||||
{
|
||||
DisplayScale = context?.Resources?.DisplayMetrics?.Density ?? 1;
|
||||
}
|
||||
|
||||
private static NativeCanvasState CreateNewState(object context)
|
||||
{
|
||||
if (_defaultFillPaint == null)
|
||||
{
|
||||
_defaultFillPaint = new global::Android.Graphics.Paint();
|
||||
_defaultFillPaint.SetARGB(255, 255, 255, 255);
|
||||
_defaultFillPaint.SetStyle(global::Android.Graphics.Paint.Style.Fill);
|
||||
_defaultFillPaint.AntiAlias = true;
|
||||
|
||||
_defaultStrokePaint = new global::Android.Graphics.Paint();
|
||||
_defaultStrokePaint.SetARGB(255, 0, 0, 0);
|
||||
_defaultStrokePaint.StrokeWidth = 1;
|
||||
_defaultStrokePaint.StrokeMiter = CanvasDefaults.DefaultMiterLimit;
|
||||
_defaultStrokePaint.SetStyle(global::Android.Graphics.Paint.Style.Stroke);
|
||||
_defaultStrokePaint.AntiAlias = true;
|
||||
|
||||
_defaultFontPaint = new TextPaint();
|
||||
_defaultFontPaint.SetARGB(255, 0, 0, 0);
|
||||
_defaultFontPaint.AntiAlias = true;
|
||||
|
||||
var arial = NativeFontService.Instance.GetTypeface("Arial");
|
||||
if (arial != null)
|
||||
_defaultFontPaint.SetTypeface(arial);
|
||||
else
|
||||
Logger.Warn("Unable to set the default font paint to Arial");
|
||||
}
|
||||
|
||||
var state = new NativeCanvasState
|
||||
{
|
||||
FillPaint = new global::Android.Graphics.Paint(_defaultFillPaint),
|
||||
StrokePaint = new global::Android.Graphics.Paint(_defaultStrokePaint),
|
||||
FontPaint = new TextPaint(_defaultFontPaint),
|
||||
FontName = NativeFontService.SystemFont
|
||||
};
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static NativeCanvasState CreateStateCopy(NativeCanvasState prototype)
|
||||
{
|
||||
return new NativeCanvasState(prototype);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_defaultFillPaint.Dispose();
|
||||
_defaultStrokePaint.Dispose();
|
||||
_defaultFontPaint.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public Canvas Canvas
|
||||
{
|
||||
get => _canvas;
|
||||
set
|
||||
{
|
||||
_canvas = null;
|
||||
ResetState();
|
||||
_canvas = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Antialias
|
||||
{
|
||||
set => CurrentState.AntiAlias = value;
|
||||
}
|
||||
|
||||
protected override float NativeStrokeSize
|
||||
{
|
||||
set => CurrentState.NativeStrokeSize = value;
|
||||
}
|
||||
|
||||
public override float MiterLimit
|
||||
{
|
||||
set => CurrentState.MiterLimit = value;
|
||||
}
|
||||
|
||||
public override float Alpha
|
||||
{
|
||||
set => CurrentState.Alpha = value;
|
||||
}
|
||||
|
||||
public override LineCap StrokeLineCap
|
||||
{
|
||||
set => CurrentState.StrokeLineCap = value;
|
||||
}
|
||||
|
||||
public override LineJoin StrokeLineJoin
|
||||
{
|
||||
set => CurrentState.StrokeLineJoin = value;
|
||||
}
|
||||
|
||||
public override Color StrokeColor
|
||||
{
|
||||
set => CurrentState.StrokeColor = value ?? Colors.Black;
|
||||
}
|
||||
|
||||
public override Color FontColor
|
||||
{
|
||||
set => CurrentState.FontColor = value ?? Colors.Black;
|
||||
}
|
||||
|
||||
public override String FontName
|
||||
{
|
||||
set => CurrentState.FontName = value ?? NativeFontService.SystemFont;
|
||||
}
|
||||
|
||||
public override float FontSize
|
||||
{
|
||||
set => CurrentState.FontSize = value;
|
||||
}
|
||||
|
||||
public override Color FillColor
|
||||
{
|
||||
set => CurrentState.FillColor = value ?? Colors.White;
|
||||
}
|
||||
|
||||
public override BlendMode BlendMode
|
||||
{
|
||||
set
|
||||
{
|
||||
/* todo: implement this
|
||||
CGBlendMode vBlendMode = CGBlendMode.Normal;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case BlendMode.Clear:
|
||||
vBlendMode = CGBlendMode.Clear;
|
||||
break;
|
||||
case BlendMode.Color:
|
||||
vBlendMode = CGBlendMode.Color;
|
||||
break;
|
||||
case BlendMode.ColorBurn:
|
||||
vBlendMode = CGBlendMode.ColorBurn;
|
||||
break;
|
||||
case BlendMode.ColorDodge:
|
||||
vBlendMode = CGBlendMode.ColorDodge;
|
||||
break;
|
||||
case BlendMode.Copy:
|
||||
vBlendMode = CGBlendMode.Copy;
|
||||
break;
|
||||
case BlendMode.Darken:
|
||||
vBlendMode = CGBlendMode.Darken;
|
||||
break;
|
||||
case BlendMode.DestinationAtop:
|
||||
vBlendMode = CGBlendMode.DestinationAtop;
|
||||
break;
|
||||
case BlendMode.DestinationIn:
|
||||
vBlendMode = CGBlendMode.DestinationIn;
|
||||
break;
|
||||
case BlendMode.DestinationOut:
|
||||
vBlendMode = CGBlendMode.DestinationOut;
|
||||
break;
|
||||
case BlendMode.DestinationOver:
|
||||
vBlendMode = CGBlendMode.DestinationOver;
|
||||
break;
|
||||
case BlendMode.Difference:
|
||||
vBlendMode = CGBlendMode.Difference;
|
||||
break;
|
||||
case BlendMode.Exclusion:
|
||||
vBlendMode = CGBlendMode.Exclusion;
|
||||
break;
|
||||
case BlendMode.HardLight:
|
||||
vBlendMode = CGBlendMode.HardLight;
|
||||
break;
|
||||
case BlendMode.Hue:
|
||||
vBlendMode = CGBlendMode.Hue;
|
||||
break;
|
||||
case BlendMode.Lighten:
|
||||
vBlendMode = CGBlendMode.Lighten;
|
||||
break;
|
||||
case BlendMode.Luminosity:
|
||||
vBlendMode = CGBlendMode.Luminosity;
|
||||
break;
|
||||
case BlendMode.Multiply:
|
||||
vBlendMode = CGBlendMode.Multiply;
|
||||
break;
|
||||
case BlendMode.Normal:
|
||||
vBlendMode = CGBlendMode.Normal;
|
||||
break;
|
||||
case BlendMode.Overlay:
|
||||
vBlendMode = CGBlendMode.Overlay;
|
||||
break;
|
||||
case BlendMode.PlusDarker:
|
||||
vBlendMode = CGBlendMode.PlusDarker;
|
||||
break;
|
||||
case BlendMode.PlusLighter:
|
||||
vBlendMode = CGBlendMode.PlusLighter;
|
||||
break;
|
||||
case BlendMode.Saturation:
|
||||
vBlendMode = CGBlendMode.Saturation;
|
||||
break;
|
||||
case BlendMode.Screen:
|
||||
vBlendMode = CGBlendMode.Screen;
|
||||
break;
|
||||
case BlendMode.SoftLight:
|
||||
vBlendMode = CGBlendMode.SoftLight;
|
||||
break;
|
||||
case BlendMode.SourceAtop:
|
||||
vBlendMode = CGBlendMode.SourceAtop;
|
||||
break;
|
||||
case BlendMode.SourceIn:
|
||||
vBlendMode = CGBlendMode.SourceIn;
|
||||
break;
|
||||
case BlendMode.SourceOut:
|
||||
vBlendMode = CGBlendMode.SourceOut;
|
||||
break;
|
||||
case BlendMode.XOR:
|
||||
vBlendMode = CGBlendMode.XOR;
|
||||
break;
|
||||
}
|
||||
|
||||
canvas.SetBlendMode(vBlendMode);*/
|
||||
|
||||
//CurrentState.FillPaint.SetXfermode(new
|
||||
}
|
||||
}
|
||||
|
||||
protected override void NativeSetStrokeDashPattern(float[] patter, float linewidth)
|
||||
{
|
||||
CurrentState.SetStrokeDashPattern(patter, linewidth);
|
||||
}
|
||||
|
||||
public override void SetToSystemFont()
|
||||
{
|
||||
CurrentState.FontName = NativeFontService.SystemFont;
|
||||
}
|
||||
|
||||
public override void SetToBoldSystemFont()
|
||||
{
|
||||
CurrentState.FontName = NativeFontService.SystemBoldFont;
|
||||
}
|
||||
|
||||
public override void SetFillPaint(Paint paint, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
if (paint == null)
|
||||
paint = System.Graphics.Colors.White.AsPaint();
|
||||
|
||||
if (_shader != null)
|
||||
{
|
||||
CurrentState.SetFillPaintShader(null);
|
||||
_shader.Dispose();
|
||||
_shader = null;
|
||||
}
|
||||
|
||||
if (paint.PaintType == PaintType.LinearGradient)
|
||||
{
|
||||
var colors = new int[paint.Stops.Length];
|
||||
var stops = new float[colors.Length];
|
||||
|
||||
GradientStop[] vStops = paint.GetSortedStops();
|
||||
|
||||
for (int i = 0; i < vStops.Length; i++)
|
||||
{
|
||||
colors[i] = vStops[i].Color.ToArgb(CurrentState.Alpha);
|
||||
stops[i] = vStops[i].Offset;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CurrentState.FillColor = Colors.White;
|
||||
_shader = new LinearGradient(x1, y1, x2, y2, colors, stops, Shader.TileMode.Clamp);
|
||||
CurrentState.SetFillPaintShader(_shader);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Debug(exc);
|
||||
FillColor = paint.BlendStartAndEndColors();
|
||||
}
|
||||
}
|
||||
else if (paint.PaintType == PaintType.RadialGradient)
|
||||
{
|
||||
var colors = new int[paint.Stops.Length];
|
||||
var stops = new float[colors.Length];
|
||||
|
||||
GradientStop[] vStops = paint.GetSortedStops();
|
||||
|
||||
for (int i = 0; i < vStops.Length; i++)
|
||||
{
|
||||
colors[i] = vStops[i].Color.ToArgb(CurrentState.Alpha);
|
||||
stops[i] = vStops[i].Offset;
|
||||
}
|
||||
|
||||
float r = Geometry.GetDistance(x1, y1, x2, y2);
|
||||
try
|
||||
{
|
||||
CurrentState.FillColor = Colors.White;
|
||||
_shader = new RadialGradient(x1, y1, r, colors, stops, Shader.TileMode.Clamp);
|
||||
CurrentState.SetFillPaintShader(_shader);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Debug(exc);
|
||||
FillColor = paint.BlendStartAndEndColors();
|
||||
}
|
||||
}
|
||||
else if (paint.PaintType == PaintType.Pattern)
|
||||
{
|
||||
var bitmap = paint.GetPatternBitmap(DisplayScale);
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentState.FillColor = Colors.White;
|
||||
CurrentState.SetFillPaintFilterBitmap(true);
|
||||
|
||||
_shader = new BitmapShader(bitmap, Shader.TileMode.Repeat, Shader.TileMode.Repeat);
|
||||
_shaderMatrix.Reset();
|
||||
_shaderMatrix.PreScale(CurrentState.ScaleX, CurrentState.ScaleY);
|
||||
_shader.SetLocalMatrix(_shaderMatrix);
|
||||
|
||||
CurrentState.SetFillPaintShader(_shader);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Debug(exc);
|
||||
FillColor = paint.BackgroundColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FillColor = paint.BackgroundColor;
|
||||
}
|
||||
}
|
||||
else if (paint.PaintType == PaintType.Image)
|
||||
{
|
||||
if (paint.Image is NativeImage image)
|
||||
{
|
||||
var bitmap = image.NativeRepresentation;
|
||||
|
||||
if (bitmap != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentState.FillColor = Colors.White;
|
||||
CurrentState.SetFillPaintFilterBitmap(true);
|
||||
|
||||
_shader = new BitmapShader(bitmap, Shader.TileMode.Repeat, Shader.TileMode.Repeat);
|
||||
_shaderMatrix.Reset();
|
||||
_shaderMatrix.PreScale(CurrentState.ScaleX, CurrentState.ScaleY);
|
||||
_shader.SetLocalMatrix(_shaderMatrix);
|
||||
|
||||
CurrentState.SetFillPaintShader(_shader);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Debug(exc);
|
||||
FillColor = paint.BackgroundColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
FillColor = Colors.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
FillColor = Colors.White;
|
||||
}
|
||||
}
|
||||
else
|
||||
FillColor = paint.StartColor;
|
||||
|
||||
//Logger.Debug("Gradient Set To: "+aPaint.PaintType);
|
||||
}
|
||||
|
||||
protected override void NativeDrawLine(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
//canvas.DrawLine (x1, y1, x2, y2, CurrentState.StrokePaintWithAlpha);
|
||||
|
||||
var nativePath = new Path();
|
||||
nativePath.MoveTo(x1, y1);
|
||||
nativePath.LineTo(x2, y2);
|
||||
_canvas.DrawPath(nativePath, CurrentState.StrokePaintWithAlpha);
|
||||
nativePath.Dispose();
|
||||
}
|
||||
|
||||
protected override void NativeDrawArc(
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
float startAngle,
|
||||
float endAngle,
|
||||
bool clockwise,
|
||||
bool closed)
|
||||
{
|
||||
while (startAngle < 0)
|
||||
{
|
||||
startAngle += 360;
|
||||
}
|
||||
|
||||
while (endAngle < 0)
|
||||
{
|
||||
endAngle += 360;
|
||||
}
|
||||
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
|
||||
float sweep = Geometry.GetSweep(startAngle, endAngle, clockwise);
|
||||
|
||||
var rect = new RectF(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
|
||||
|
||||
startAngle *= -1;
|
||||
if (!clockwise)
|
||||
{
|
||||
sweep *= -1;
|
||||
}
|
||||
|
||||
if (closed)
|
||||
{
|
||||
var nativePath = new Path();
|
||||
nativePath.AddArc(rect, startAngle, sweep);
|
||||
nativePath.Close();
|
||||
_canvas.DrawPath(nativePath, CurrentState.StrokePaintWithAlpha);
|
||||
nativePath.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
_canvas.DrawArc(rect, startAngle, sweep, false, CurrentState.StrokePaintWithAlpha);
|
||||
}
|
||||
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
public override void FillArc(
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
float startAngle,
|
||||
float endAngle,
|
||||
bool clockwise)
|
||||
{
|
||||
while (startAngle < 0)
|
||||
{
|
||||
startAngle += 360;
|
||||
}
|
||||
|
||||
while (endAngle < 0)
|
||||
{
|
||||
endAngle += 360;
|
||||
}
|
||||
|
||||
var sweep = Geometry.GetSweep(startAngle, endAngle, clockwise);
|
||||
var rect = new RectF(x, y, x + width, y + height);
|
||||
|
||||
startAngle *= -1;
|
||||
if (!clockwise)
|
||||
{
|
||||
sweep *= -1;
|
||||
}
|
||||
|
||||
_canvas.DrawArc(rect, startAngle, sweep, false, CurrentState.FillPaintWithAlpha);
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
protected override void NativeDrawRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
float rectX = 0, rectY = 0, rectWidth = 0, rectHeight = 0;
|
||||
|
||||
float strokeSize = CurrentState.ScaledStrokeSize;
|
||||
if (strokeSize == 0)
|
||||
return;
|
||||
|
||||
rectX = x;
|
||||
rectY = y;
|
||||
rectWidth = width;
|
||||
rectHeight = height;
|
||||
|
||||
_canvas.DrawRect(rectX, rectY, rectX + rectWidth, rectY + rectHeight, CurrentState.StrokePaintWithAlpha);
|
||||
}
|
||||
|
||||
public override void FillRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
|
||||
_canvas.DrawRect(rectX, rectY, rectX + rectWidth, rectY + rectHeight, CurrentState.FillPaintWithAlpha);
|
||||
}
|
||||
|
||||
protected override void NativeDrawRoundedRectangle(
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
float aCornerRadius)
|
||||
{
|
||||
// These values work for a stroke location of center.
|
||||
float strokeSize = CurrentState.ScaledStrokeSize;
|
||||
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
var radius = aCornerRadius;
|
||||
|
||||
var rect = new RectF(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
|
||||
_canvas.DrawRoundRect(rect, radius, radius, CurrentState.StrokePaintWithAlpha);
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
public override void FillRoundedRectangle(float x, float y, float width, float height, float aCornerRadius)
|
||||
{
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
var radius = aCornerRadius;
|
||||
|
||||
var rect = new RectF(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
|
||||
_canvas.DrawRoundRect(rect, radius, radius, CurrentState.FillPaintWithAlpha);
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
protected override void NativeDrawOval(float x, float y, float width, float height)
|
||||
{
|
||||
// These values work for a stroke location of center.
|
||||
float strokeSize = CurrentState.ScaledStrokeSize;
|
||||
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
|
||||
var rect = new RectF(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
|
||||
_canvas.DrawOval(rect, CurrentState.StrokePaintWithAlpha);
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
public override void FillOval(float x, float y, float width, float height)
|
||||
{
|
||||
/* todo: support gradients here */
|
||||
|
||||
// These values work for a stroke location of center.
|
||||
var rectX = x;
|
||||
var rectY = y;
|
||||
var rectWidth = width;
|
||||
var rectHeight = height;
|
||||
|
||||
var rect = new RectF(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
|
||||
_canvas.DrawOval(rect, CurrentState.FillPaintWithAlpha);
|
||||
rect.Dispose();
|
||||
}
|
||||
|
||||
public override void SubtractFromClip(float x, float y, float width, float height)
|
||||
{
|
||||
_canvas.ClipRect(x, y, x + width, y + height, Region.Op.Difference);
|
||||
}
|
||||
|
||||
protected override void NativeDrawPath(PathF aPath)
|
||||
{
|
||||
var nativePath = aPath.AsAndroidPath();
|
||||
_canvas.DrawPath(nativePath, CurrentState.StrokePaintWithAlpha);
|
||||
nativePath.Dispose();
|
||||
}
|
||||
|
||||
public override void ClipPath(PathF path, WindingMode windingMode = WindingMode.NonZero)
|
||||
{
|
||||
var nativePath = path.AsAndroidPath();
|
||||
nativePath.SetFillType(windingMode == WindingMode.NonZero ? Path.FillType.Winding : Path.FillType.EvenOdd);
|
||||
_canvas.ClipPath(nativePath);
|
||||
}
|
||||
|
||||
public override void ClipRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
_canvas.ClipRect(x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
public override void FillPath(PathF path, WindingMode windingMode)
|
||||
{
|
||||
var nativePath = path.AsAndroidPath();
|
||||
nativePath.SetFillType(windingMode == WindingMode.NonZero ? Path.FillType.Winding : Path.FillType.EvenOdd);
|
||||
_canvas.DrawPath(nativePath, CurrentState.FillPaintWithAlpha);
|
||||
nativePath.Dispose();
|
||||
}
|
||||
|
||||
public override void DrawString(string value, float x, float y, HorizontalAlignment horizAlignment)
|
||||
{
|
||||
if (horizAlignment == HorizontalAlignment.Left)
|
||||
DrawString(value, x, y);
|
||||
else if (horizAlignment == HorizontalAlignment.Right)
|
||||
{
|
||||
SizeF vSize = NativeGraphicsService.Instance.GetStringSize(
|
||||
value,
|
||||
CurrentState.FontName,
|
||||
CurrentState.ScaledFontSize);
|
||||
x -= vSize.Width;
|
||||
DrawString(value, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
SizeF vSize = NativeGraphicsService.Instance.GetStringSize(
|
||||
value,
|
||||
CurrentState.FontName,
|
||||
CurrentState.ScaledFontSize);
|
||||
x -= vSize.Width / 2f;
|
||||
DrawString(value, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawString(string value, float x, float y)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
_canvas.Save();
|
||||
_canvas.Translate(x, y - CurrentState.ScaledFontSize);
|
||||
var layout = new StaticLayout(
|
||||
value,
|
||||
CurrentState.FontPaint,
|
||||
512,
|
||||
Layout.Alignment.AlignNormal,
|
||||
1f,
|
||||
0f,
|
||||
false);
|
||||
layout.Draw(_canvas);
|
||||
_canvas.Restore();
|
||||
}
|
||||
|
||||
public override void DrawString(
|
||||
string value,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
HorizontalAlignment horizAlignment,
|
||||
VerticalAlignment vertAlignment,
|
||||
TextFlow textFlow = TextFlow.ClipBounds,
|
||||
float lineSpacingAdjustment = 0)
|
||||
{
|
||||
if (value == null || value.Length == 0 || width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
_canvas.Save();
|
||||
|
||||
var alignment = Layout.Alignment.AlignNormal;
|
||||
if (horizAlignment == HorizontalAlignment.Center)
|
||||
{
|
||||
alignment = Layout.Alignment.AlignCenter;
|
||||
}
|
||||
else if (horizAlignment == HorizontalAlignment.Right)
|
||||
{
|
||||
alignment = Layout.Alignment.AlignOpposite;
|
||||
}
|
||||
|
||||
var layout = TextLayoutUtils.CreateLayout(value, CurrentState.FontPaint, (int) width, alignment);
|
||||
var offset = layout.GetOffsetsToDrawText(x, y, width, height, horizAlignment, vertAlignment);
|
||||
_canvas.Translate(offset.Width, offset.Height);
|
||||
layout.Draw(_canvas);
|
||||
_canvas.Restore();
|
||||
}
|
||||
|
||||
public override void DrawText(IAttributedText value, float x, float y, float width, float height)
|
||||
{
|
||||
_canvas.Save();
|
||||
var span = value.AsSpannableString();
|
||||
var layout = TextLayoutUtils.CreateLayoutForSpannedString(span, CurrentState.FontPaint, (int) width, Layout.Alignment.AlignNormal);
|
||||
var offset = layout.GetOffsetsToDrawText(x, y, width, height, HorizontalAlignment.Left, VerticalAlignment.Top);
|
||||
_canvas.Translate(offset.Width, offset.Height);
|
||||
layout.Draw(_canvas);
|
||||
layout.Dispose();
|
||||
span.Dispose();
|
||||
_canvas.Restore();
|
||||
}
|
||||
|
||||
public override void ResetState()
|
||||
{
|
||||
base.ResetState();
|
||||
|
||||
if (_shader != null)
|
||||
{
|
||||
_shader.Dispose();
|
||||
_shader = null;
|
||||
}
|
||||
|
||||
CurrentState.Reset(_defaultFontPaint, _defaultFillPaint, _defaultStrokePaint);
|
||||
}
|
||||
|
||||
public override bool RestoreState()
|
||||
{
|
||||
if (_shader != null)
|
||||
{
|
||||
_shader.Dispose();
|
||||
_shader = null;
|
||||
}
|
||||
|
||||
return base.RestoreState();
|
||||
}
|
||||
|
||||
protected override void StateRestored(NativeCanvasState state)
|
||||
{
|
||||
_canvas.Restore();
|
||||
}
|
||||
|
||||
public override void SetShadow(SizeF offset, float blur, Color color)
|
||||
{
|
||||
SizeF actualOffset = offset;
|
||||
var sx = actualOffset.Width;
|
||||
var sy = actualOffset.Height;
|
||||
|
||||
if (color == null)
|
||||
{
|
||||
var actualColor = Colors.Black.AsColorMultiplyAlpha(CurrentState.Alpha);
|
||||
CurrentState.SetShadow(blur, sx, sy, actualColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
var actualColor = color.AsColorMultiplyAlpha(CurrentState.Alpha);
|
||||
CurrentState.SetShadow(blur, sx, sy, actualColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void NativeRotate(float degrees, float radians, float x, float y)
|
||||
{
|
||||
_canvas.Rotate(degrees, x, y);
|
||||
}
|
||||
|
||||
protected override void NativeRotate(float degrees, float radians)
|
||||
{
|
||||
_canvas.Rotate(degrees);
|
||||
}
|
||||
|
||||
protected override void NativeScale(float xFactor, float yFactor)
|
||||
{
|
||||
CurrentState.SetScale(Math.Abs(xFactor), Math.Abs(yFactor));
|
||||
if (xFactor < 0 || yFactor < 0)
|
||||
_canvas.Scale(xFactor < 0 ? -1 : 1, yFactor < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
protected override void NativeTranslate(float tx, float ty)
|
||||
{
|
||||
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
var matrix = new Matrix(_canvas.Matrix);
|
||||
matrix.PostConcat(transform.AsMatrix());
|
||||
_canvas.Matrix = matrix;
|
||||
}
|
||||
|
||||
public override void SaveState()
|
||||
{
|
||||
_canvas.Save();
|
||||
base.SaveState();
|
||||
}
|
||||
|
||||
public void SetBlur(float radius)
|
||||
{
|
||||
CurrentState.SetBlur(radius);
|
||||
}
|
||||
|
||||
public override void DrawImage(IImage image, float x, float y, float width, float height)
|
||||
{
|
||||
if (image is NativeImage mdimage)
|
||||
{
|
||||
var bitmap = mdimage.NativeRepresentation;
|
||||
if (bitmap != null)
|
||||
{
|
||||
var scaleX = CurrentState.ScaleX < 0 ? -1 : 1;
|
||||
var scaleY = CurrentState.ScaleY < 0 ? -1 : 1;
|
||||
|
||||
_canvas.Save();
|
||||
//canvas.Scale (scaleX, scaleY);
|
||||
var srcRect = new Rect(0, 0, bitmap.Width, bitmap.Height);
|
||||
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
width *= scaleX;
|
||||
height *= scaleY;
|
||||
|
||||
var rx1 = Math.Min(x, x + width);
|
||||
var ry1 = Math.Min(y, y + height);
|
||||
var rx2 = Math.Max(x, x + width);
|
||||
var ry2 = Math.Max(y, y + height);
|
||||
|
||||
var destRect = new RectF(rx1, ry1, rx2, ry2);
|
||||
var paint = CurrentState.GetImagePaint(1, 1);
|
||||
_canvas.DrawBitmap(bitmap, srcRect, destRect, paint);
|
||||
paint?.Dispose();
|
||||
_canvas.Restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
using Color = Android.Graphics.Color;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeCanvasState : CanvasState, IBlurrableCanvas
|
||||
{
|
||||
public float Alpha = 1;
|
||||
private global::Android.Graphics.Paint _fillPaint;
|
||||
private global::Android.Graphics.Paint _strokePaint;
|
||||
private string _fontName = "Arial";
|
||||
private TextPaint _fontPaint;
|
||||
private float _fontSize = 10f;
|
||||
private float _scaleX = 1;
|
||||
private float _scaleY = 1;
|
||||
private bool _typefaceInvalid;
|
||||
private bool _isBlurred;
|
||||
private float _blurRadius;
|
||||
private BlurMaskFilter _blurFilter;
|
||||
private bool _shadowed;
|
||||
private global::Android.Graphics.Color _shadowColor;
|
||||
private float _shadowX;
|
||||
private float _shadowY;
|
||||
private float _shadowBlur;
|
||||
|
||||
private Color _strokeColor = Colors.Black;
|
||||
private Color _fillColor = Colors.White;
|
||||
private Color _fontColor = Colors.Black;
|
||||
|
||||
public NativeCanvasState()
|
||||
{
|
||||
}
|
||||
|
||||
public NativeCanvasState(NativeCanvasState prototype) : base(prototype)
|
||||
{
|
||||
_strokeColor = prototype._strokeColor;
|
||||
_fillColor = prototype._fillColor;
|
||||
_fontColor = prototype._fontColor;
|
||||
|
||||
_fontPaint = new TextPaint(prototype.FontPaint);
|
||||
_fillPaint = new global::Android.Graphics.Paint(prototype.FillPaint);
|
||||
_strokePaint = new global::Android.Graphics.Paint(prototype.StrokePaint);
|
||||
_fontName = prototype._fontName;
|
||||
_fontSize = prototype._fontSize;
|
||||
Alpha = prototype.Alpha;
|
||||
_scaleX = prototype._scaleX;
|
||||
_scaleY = prototype._scaleY;
|
||||
_typefaceInvalid = false;
|
||||
|
||||
_isBlurred = prototype._isBlurred;
|
||||
_blurRadius = prototype._blurRadius;
|
||||
|
||||
_shadowed = prototype._shadowed;
|
||||
_shadowColor = prototype._shadowColor;
|
||||
_shadowX = prototype._shadowX;
|
||||
_shadowY = prototype._shadowY;
|
||||
_shadowBlur = prototype._shadowBlur;
|
||||
}
|
||||
|
||||
public Color StrokeColor
|
||||
{
|
||||
get => _strokeColor;
|
||||
set => _strokeColor = value;
|
||||
}
|
||||
|
||||
public Color FillColor
|
||||
{
|
||||
get => _fillColor;
|
||||
set => _fillColor = value;
|
||||
}
|
||||
|
||||
public Color FontColor
|
||||
{
|
||||
get => _fontColor;
|
||||
set
|
||||
{
|
||||
_fontColor = value;
|
||||
FontPaint.Color = value != null ? _fontColor.AsColor() : global::Android.Graphics.Color.Black;
|
||||
}
|
||||
}
|
||||
|
||||
public LineCap StrokeLineCap
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == LineCap.Butt)
|
||||
StrokePaint.StrokeCap = global::Android.Graphics.Paint.Cap.Butt;
|
||||
else if (value == LineCap.Round)
|
||||
StrokePaint.StrokeCap = global::Android.Graphics.Paint.Cap.Round;
|
||||
else if (value == LineCap.Square)
|
||||
StrokePaint.StrokeCap = global::Android.Graphics.Paint.Cap.Square;
|
||||
}
|
||||
}
|
||||
|
||||
public LineJoin StrokeLineJoin
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == LineJoin.Miter)
|
||||
StrokePaint.StrokeJoin = global::Android.Graphics.Paint.Join.Miter;
|
||||
else if (value == LineJoin.Round)
|
||||
StrokePaint.StrokeJoin = global::Android.Graphics.Paint.Join.Round;
|
||||
else if (value == LineJoin.Bevel)
|
||||
StrokePaint.StrokeJoin = global::Android.Graphics.Paint.Join.Bevel;
|
||||
}
|
||||
}
|
||||
|
||||
public float MiterLimit
|
||||
{
|
||||
set => StrokePaint.StrokeMiter = value;
|
||||
}
|
||||
|
||||
public void SetStrokeDashPattern(float[] pattern, float strokeSize)
|
||||
{
|
||||
if (pattern == null || pattern.Length == 0 || strokeSize == 0)
|
||||
{
|
||||
StrokePaint.SetPathEffect(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
float scaledStrokeSize = strokeSize * ScaleX;
|
||||
|
||||
if (scaledStrokeSize == 1)
|
||||
{
|
||||
StrokePaint.SetPathEffect(new DashPathEffect(pattern, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
var scaledPattern = new float[pattern.Length];
|
||||
for (int i = 0; i < pattern.Length; i++)
|
||||
scaledPattern[i] = pattern[i] * scaledStrokeSize;
|
||||
StrokePaint.SetPathEffect(new DashPathEffect(scaledPattern, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AntiAlias
|
||||
{
|
||||
set => StrokePaint.AntiAlias = value;
|
||||
}
|
||||
|
||||
public bool IsBlurred => _isBlurred;
|
||||
|
||||
public float BlurRadius => _blurRadius;
|
||||
|
||||
public void SetBlur(float aRadius)
|
||||
{
|
||||
if (aRadius != _blurRadius)
|
||||
{
|
||||
if (_blurFilter != null)
|
||||
{
|
||||
_blurFilter.Dispose();
|
||||
_blurFilter = null;
|
||||
}
|
||||
|
||||
if (aRadius > 0)
|
||||
{
|
||||
_isBlurred = true;
|
||||
_blurRadius = aRadius;
|
||||
|
||||
_blurFilter = new BlurMaskFilter(_blurRadius, BlurMaskFilter.Blur.Normal);
|
||||
|
||||
_fillPaint?.SetMaskFilter(_blurFilter);
|
||||
_strokePaint?.SetMaskFilter(_blurFilter);
|
||||
_fontPaint?.SetMaskFilter(_blurFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_isBlurred = false;
|
||||
_blurRadius = 0;
|
||||
|
||||
_fillPaint?.SetMaskFilter(null);
|
||||
_strokePaint?.SetMaskFilter(null);
|
||||
_fontPaint?.SetMaskFilter(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float NativeStrokeSize
|
||||
{
|
||||
set => StrokePaint.StrokeWidth = value * _scaleX;
|
||||
}
|
||||
|
||||
public float FontSize
|
||||
{
|
||||
set
|
||||
{
|
||||
_fontSize = value;
|
||||
FontPaint.TextSize = _fontSize * _scaleX;
|
||||
}
|
||||
}
|
||||
|
||||
public string FontName
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_fontName != value && _fontName != null && !_fontName.Equals(value))
|
||||
{
|
||||
_fontName = value;
|
||||
_typefaceInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
get => _fontName;
|
||||
}
|
||||
|
||||
public TextPaint FontPaint
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fontPaint == null)
|
||||
{
|
||||
_fontPaint = new TextPaint();
|
||||
_fontPaint.SetARGB(1, 0, 0, 0);
|
||||
_fontPaint.AntiAlias = true;
|
||||
_fontPaint.SetTypeface(NativeFontService.Instance.GetTypeface("Arial"));
|
||||
}
|
||||
|
||||
if (_typefaceInvalid)
|
||||
{
|
||||
_fontPaint.SetTypeface(NativeFontService.Instance.GetTypeface(_fontName));
|
||||
_typefaceInvalid = false;
|
||||
}
|
||||
|
||||
return _fontPaint;
|
||||
}
|
||||
|
||||
set => _fontPaint = value;
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint FillPaint
|
||||
{
|
||||
private get
|
||||
{
|
||||
if (_fillPaint == null)
|
||||
{
|
||||
_fillPaint = new global::Android.Graphics.Paint();
|
||||
_fillPaint.SetARGB(1, 1, 1, 1);
|
||||
_fillPaint.SetStyle(global::Android.Graphics.Paint.Style.Fill);
|
||||
_fillPaint.AntiAlias = true;
|
||||
}
|
||||
|
||||
return _fillPaint;
|
||||
}
|
||||
|
||||
set { _fillPaint = value; }
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint StrokePaint
|
||||
{
|
||||
private get
|
||||
{
|
||||
if (_strokePaint == null)
|
||||
{
|
||||
var paint = new global::Android.Graphics.Paint();
|
||||
paint.SetARGB(1, 0, 0, 0);
|
||||
paint.StrokeWidth = 1;
|
||||
paint.StrokeMiter = CanvasDefaults.DefaultMiterLimit;
|
||||
paint.SetStyle(global::Android.Graphics.Paint.Style.Stroke);
|
||||
paint.AntiAlias = true;
|
||||
|
||||
_strokePaint = paint;
|
||||
|
||||
return paint;
|
||||
}
|
||||
|
||||
return _strokePaint;
|
||||
}
|
||||
|
||||
set { _strokePaint = value; }
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint StrokePaintWithAlpha
|
||||
{
|
||||
get
|
||||
{
|
||||
var paint = StrokePaint;
|
||||
|
||||
var r = (int) (_strokeColor.Red * 255f);
|
||||
var g = (int) (_strokeColor.Green * 255f);
|
||||
var b = (int) (_strokeColor.Blue * 255f);
|
||||
var a = (int) (_strokeColor.Alpha * 255f * Alpha);
|
||||
|
||||
paint.SetARGB(a, r, g, b);
|
||||
return paint;
|
||||
}
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint FillPaintWithAlpha
|
||||
{
|
||||
get
|
||||
{
|
||||
var paint = FillPaint;
|
||||
|
||||
var r = (int) (_fillColor.Red * 255f);
|
||||
var g = (int) (_fillColor.Green * 255f);
|
||||
var b = (int) (_fillColor.Blue * 255f);
|
||||
var a = (int) (_fillColor.Alpha * 255f * Alpha);
|
||||
|
||||
paint.SetARGB(a, r, g, b);
|
||||
return paint;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFillPaintShader(Shader shader)
|
||||
{
|
||||
FillPaint.SetShader(shader);
|
||||
}
|
||||
|
||||
public void SetFillPaintFilterBitmap(bool value)
|
||||
{
|
||||
FillPaint.FilterBitmap = value;
|
||||
}
|
||||
|
||||
public float ScaledStrokeSize => StrokeSize * _scaleX;
|
||||
|
||||
public float ScaledFontSize => _fontSize * _scaleX;
|
||||
|
||||
public float ScaleX => _scaleX;
|
||||
|
||||
public float ScaleY => _scaleY;
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_fontPaint != null)
|
||||
{
|
||||
_fontPaint.Dispose();
|
||||
_fontPaint = null;
|
||||
}
|
||||
|
||||
if (_strokePaint != null)
|
||||
{
|
||||
_strokePaint.Dispose();
|
||||
_strokePaint = null;
|
||||
}
|
||||
|
||||
if (_fillPaint != null)
|
||||
{
|
||||
_fillPaint.Dispose();
|
||||
_fillPaint = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void SetShadow(float blur, float sx, float sy, global::Android.Graphics.Color color)
|
||||
{
|
||||
FillPaint.SetShadowLayer(blur, sx, sy, color);
|
||||
StrokePaint.SetShadowLayer(blur, sx, sy, color);
|
||||
FontPaint.SetShadowLayer(blur, sx, sy, color);
|
||||
|
||||
_shadowed = true;
|
||||
_shadowBlur = blur;
|
||||
_shadowX = sx;
|
||||
_shadowY = sy;
|
||||
_shadowColor = color;
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint GetShadowPaint(float sx, float sy)
|
||||
{
|
||||
if (_shadowed)
|
||||
{
|
||||
var shadowPaint = new global::Android.Graphics.Paint();
|
||||
shadowPaint.SetARGB(255, 0, 0, 0);
|
||||
shadowPaint.SetStyle(global::Android.Graphics.Paint.Style.Fill);
|
||||
shadowPaint.AntiAlias = true;
|
||||
shadowPaint.SetShadowLayer(_shadowBlur, _shadowX * sx, _shadowY * sy, _shadowColor);
|
||||
shadowPaint.Alpha = (int) (Alpha * 255f);
|
||||
return shadowPaint;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public global::Android.Graphics.Paint GetImagePaint(float sx, float sy)
|
||||
{
|
||||
var imagePaint = GetShadowPaint(sx, sy);
|
||||
if (imagePaint == null && Alpha < 1)
|
||||
{
|
||||
imagePaint = new global::Android.Graphics.Paint();
|
||||
imagePaint.SetARGB(255, 0, 0, 0);
|
||||
imagePaint.SetStyle(global::Android.Graphics.Paint.Style.Fill);
|
||||
imagePaint.AntiAlias = true;
|
||||
imagePaint.Alpha = (int) (Alpha * 255f);
|
||||
}
|
||||
|
||||
return imagePaint;
|
||||
}
|
||||
|
||||
public void SetScale(float sx, float sy)
|
||||
{
|
||||
_scaleX = _scaleX * sx;
|
||||
_scaleY = _scaleY * sy;
|
||||
|
||||
StrokePaint.StrokeWidth = StrokeSize * _scaleX;
|
||||
FontPaint.TextSize = _fontSize * _scaleX;
|
||||
}
|
||||
|
||||
public void Reset(global::Android.Graphics.Paint aFontPaint, global::Android.Graphics.Paint aFillPaint, global::Android.Graphics.Paint aStrokePaint)
|
||||
{
|
||||
_fontPaint?.Dispose();
|
||||
_fontPaint = new TextPaint(aFontPaint);
|
||||
|
||||
_fillPaint?.Dispose();
|
||||
_fillPaint = new global::Android.Graphics.Paint(aFillPaint);
|
||||
|
||||
_strokePaint?.Dispose();
|
||||
_strokePaint = new global::Android.Graphics.Paint(aStrokePaint);
|
||||
|
||||
_fontName = "Arial";
|
||||
_fontSize = 10f;
|
||||
Alpha = 1;
|
||||
_scaleX = 1;
|
||||
_scaleY = 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeFontFamily : IFontFamily, IComparable<IFontFamily>, IComparable
|
||||
{
|
||||
private readonly string _name;
|
||||
private IFontStyle[] _fontStyles;
|
||||
private readonly List<IFontStyle> _styleList = new List<IFontStyle>();
|
||||
|
||||
public NativeFontFamily(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public string Name => _name;
|
||||
|
||||
public IFontStyle[] GetFontStyles()
|
||||
{
|
||||
return _fontStyles ?? (_fontStyles = InitializeFontStyles());
|
||||
}
|
||||
|
||||
internal void AddStyle(NativeFontStyle style)
|
||||
{
|
||||
_fontStyles = null;
|
||||
_styleList.Add(style);
|
||||
}
|
||||
|
||||
internal bool HasStyle(string style)
|
||||
{
|
||||
return _styleList.Exists(s => s.Name.Equals(style));
|
||||
}
|
||||
|
||||
private IFontStyle[] InitializeFontStyles()
|
||||
{
|
||||
_styleList.Sort();
|
||||
return _styleList.ToArray();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
if (obj.GetType() != typeof(NativeFontFamily))
|
||||
return false;
|
||||
NativeFontFamily other = (NativeFontFamily) obj;
|
||||
return _name == other._name;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return _name != null ? _name.GetHashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public int CompareTo(IFontFamily other)
|
||||
{
|
||||
return String.Compare(_name, other.Name, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is IFontFamily other)
|
||||
return CompareTo(other);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Android.App;
|
||||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeFontService : AbstractFontService
|
||||
{
|
||||
public static Boolean FontAliasingEnabled { get; set; } = true;
|
||||
|
||||
public const string SystemFont = "System";
|
||||
public const string SystemBoldFont = "System-Bold";
|
||||
|
||||
public static NativeFontService Instance = new NativeFontService();
|
||||
|
||||
private IFontFamily[] _fontFamilies;
|
||||
private readonly Dictionary<string, Typeface> _typeFaces = new Dictionary<string, Typeface>();
|
||||
|
||||
public NativeFontService() : base("Droid Sans", "Roboto", "Aramo")
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly List<string> ExcludeList = new List<string>
|
||||
{
|
||||
"AndroidClock",
|
||||
"AndroidClock-Large",
|
||||
"Clockopia",
|
||||
"Droid Sans Fallback",
|
||||
"Lohit Bengali",
|
||||
"Lohit Devanagari",
|
||||
"Lohit Tamil",
|
||||
"Roboto Test1",
|
||||
"GS_Thai",
|
||||
"GS45_Arab(AndroidOS)",
|
||||
"Symbol Std"
|
||||
};
|
||||
|
||||
public override IFontFamily[] GetFontFamilies()
|
||||
{
|
||||
return _fontFamilies ?? (_fontFamilies = InitializeFonts());
|
||||
}
|
||||
|
||||
public IFontFamily[] InitializeFonts()
|
||||
{
|
||||
var families = new Dictionary<string, NativeFontFamily>();
|
||||
var familyList = new List<IFontFamily>();
|
||||
var analyzer = new FontAnalyzer();
|
||||
|
||||
var assembly = typeof(NativeFontService).Assembly;
|
||||
var resources = assembly.GetManifestResourceNames();
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
if (resource.EndsWith("tf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var path = resource.Split('.');
|
||||
var id = path[path.Length - 2];
|
||||
var parts = id.Split('-');
|
||||
var familyName = parts[0];
|
||||
var type = parts[1];
|
||||
|
||||
if (familyName.StartsWith("TeXGyre", StringComparison.InvariantCulture))
|
||||
familyName = "TeX Gyre " + familyName.Substring(7);
|
||||
|
||||
if (!families.TryGetValue(familyName, out var family))
|
||||
{
|
||||
family = new NativeFontFamily(familyName);
|
||||
families[familyName] = family;
|
||||
familyList.Add(family);
|
||||
}
|
||||
|
||||
var weight = FontUtils.Regular;
|
||||
if (type.Contains("Bold"))
|
||||
weight = FontUtils.Bold;
|
||||
|
||||
var styleType = FontStyleType.Normal;
|
||||
if (type.Contains("Italic"))
|
||||
styleType = FontStyleType.Italic;
|
||||
|
||||
var fullName = $"{familyName} {type}";
|
||||
if ("Regular".Equals(type))
|
||||
fullName = familyName;
|
||||
|
||||
var style = new NativeFontStyle(family, id, type, fullName, weight, styleType, resource, true);
|
||||
family.AddStyle(style);
|
||||
|
||||
if (FontAliasingEnabled)
|
||||
{
|
||||
var suffix = string.Empty;
|
||||
var italic = "Italic";
|
||||
if ("Arimo".Equals(familyName))
|
||||
{
|
||||
familyName = "Arial";
|
||||
id = "Arial";
|
||||
suffix = "MT";
|
||||
}
|
||||
else if ("Tinos".Equals(familyName))
|
||||
{
|
||||
familyName = "Times New Roman";
|
||||
id = "TimesNewRomanPS";
|
||||
suffix = "MT";
|
||||
}
|
||||
else if ("Cousine".Equals(familyName))
|
||||
{
|
||||
familyName = "Courier New";
|
||||
id = "CourierNewPS";
|
||||
suffix = "MT";
|
||||
}
|
||||
else if ("TeX Gyre Termes".Equals(familyName))
|
||||
{
|
||||
familyName = "Times";
|
||||
id = "Times";
|
||||
suffix = "";
|
||||
}
|
||||
else if ("TeX Gyre Heros".Equals(familyName))
|
||||
{
|
||||
familyName = "Helvetica";
|
||||
id = "Helvetica";
|
||||
italic = "Oblique";
|
||||
suffix = "";
|
||||
}
|
||||
else if ("TeX Gyre Cursor".Equals(familyName))
|
||||
{
|
||||
familyName = "Courier";
|
||||
id = "Courier";
|
||||
italic = "Oblique";
|
||||
suffix = "";
|
||||
}
|
||||
|
||||
if (!families.TryGetValue(familyName, out family))
|
||||
{
|
||||
family = new NativeFontFamily(familyName);
|
||||
families[familyName] = family;
|
||||
familyList.Add(family);
|
||||
}
|
||||
|
||||
fullName = $"{familyName} {type}";
|
||||
if ("Regular".Equals(type))
|
||||
fullName = familyName;
|
||||
|
||||
if (styleType == FontStyleType.Italic)
|
||||
{
|
||||
if (weight == FontUtils.Bold)
|
||||
id = id + "-" + "Bold" + italic + suffix;
|
||||
else
|
||||
id = id + "-" + italic + suffix;
|
||||
|
||||
if ("Oblique".Equals(italic))
|
||||
styleType = FontStyleType.Oblique;
|
||||
}
|
||||
else if (weight == FontUtils.Bold)
|
||||
{
|
||||
id = id + "-" + "Bold" + suffix;
|
||||
}
|
||||
|
||||
style = new NativeFontStyle(family, id, type, fullName, weight, styleType, resource, true);
|
||||
family.AddStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string searchPath in FontSearchPaths)
|
||||
{
|
||||
if (searchPath != null)
|
||||
{
|
||||
var searchDirectory = new DirectoryInfo(searchPath);
|
||||
|
||||
if (searchDirectory.Exists)
|
||||
{
|
||||
var files = searchDirectory.GetFiles();
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fontInfo = analyzer.GetFontInfo(file.FullName);
|
||||
|
||||
if (fontInfo != null)
|
||||
{
|
||||
if (IsValidFont(fontInfo))
|
||||
{
|
||||
if (!families.TryGetValue(fontInfo.Family, out var family))
|
||||
{
|
||||
family = new NativeFontFamily(fontInfo.Family);
|
||||
families[fontInfo.Family] = family;
|
||||
familyList.Add(family);
|
||||
}
|
||||
|
||||
if (!family.HasStyle(fontInfo.Style))
|
||||
{
|
||||
var id = fontInfo.FullName;
|
||||
var name = fontInfo.Style;
|
||||
var weight = FontUtils.GetFontWeight(name);
|
||||
var styleType = FontUtils.GetStyleType(name);
|
||||
|
||||
string fullName = fontInfo.Family;
|
||||
if (!"Regular".Equals(fontInfo.Style))
|
||||
fullName = $"{fontInfo.Family} {name}";
|
||||
|
||||
var style = new NativeFontStyle(family, id, name, fullName, weight, styleType, fontInfo.Path);
|
||||
family.AddStyle(style);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Duplicate style found for font: {0} {1}", fontInfo.Family, fontInfo.Style);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Unable to load the font info for the font file: " + file.FullName);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Info("Unable to handle the font file: " + file.FullName, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
familyList.Sort();
|
||||
return familyList.ToArray();
|
||||
}
|
||||
|
||||
protected bool IsValidFont(FontInfo fontInfo)
|
||||
{
|
||||
if (fontInfo.Family == null)
|
||||
return false;
|
||||
|
||||
if (ExcludeList.Contains(fontInfo.Family))
|
||||
return false;
|
||||
|
||||
if (fontInfo.Family.StartsWith("Samsung"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of paths that the font service should search for font files.
|
||||
/// </summary>
|
||||
/// <value>The font search paths.</value>
|
||||
protected string[] FontSearchPaths => new[] {"/system/fonts", "/system/font", "/data/fonts", UserFontsPath};
|
||||
|
||||
/// <summary>
|
||||
/// The path where application user added fonts are (or should be) installed.
|
||||
/// </summary>
|
||||
/// <value>The application fonts path.</value>
|
||||
protected string UserFontsPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string fontsPath = null;
|
||||
|
||||
try
|
||||
{
|
||||
var externalFilesFir = Application.Context.GetExternalFilesDir(null);
|
||||
var absolutePath = externalFilesFir.AbsolutePath;
|
||||
fontsPath = System.IO.Path.Combine(absolutePath, "Fonts");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Debug(exc);
|
||||
}
|
||||
|
||||
return fontsPath;
|
||||
}
|
||||
}
|
||||
|
||||
public Typeface GetTypeface(string name)
|
||||
{
|
||||
if (name == null || SystemFont.Equals(name))
|
||||
return Typeface.Default;
|
||||
|
||||
if (SystemBoldFont.Equals(name))
|
||||
return Typeface.DefaultBold;
|
||||
|
||||
if (GetFontStyleById(name) is NativeFontStyle fontStyle)
|
||||
{
|
||||
if (!_typeFaces.TryGetValue(name, out var typeface))
|
||||
{
|
||||
string path;
|
||||
|
||||
if (fontStyle.Resource)
|
||||
{
|
||||
var resource = fontStyle.Path;
|
||||
var resourceParts = resource.Split('.');
|
||||
var fileName = $"{resourceParts[resourceParts.Length - 2]}.{resourceParts[resourceParts.Length - 1]}";
|
||||
|
||||
path = System.IO.Path.Combine(UserFontsPath, fileName);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
if (!Directory.Exists(UserFontsPath))
|
||||
Directory.CreateDirectory(UserFontsPath);
|
||||
|
||||
var assembly = typeof(NativeFontService).Assembly;
|
||||
using (var stream = assembly.GetManifestResourceStream(resource))
|
||||
{
|
||||
using (var outputStream = new FileStream(path, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
stream?.CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = fontStyle.Path;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
typeface = Typeface.CreateFromFile(path);
|
||||
if (typeface != null)
|
||||
_typeFaces[name] = typeface;
|
||||
}
|
||||
catch (Java.Lang.RuntimeException exc)
|
||||
{
|
||||
Logger.Info("Unable to load font from the file: " + fontStyle.Path, exc);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Info("Unable to load font from the file: " + fontStyle.Path, exc);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeface != null)
|
||||
return typeface;
|
||||
}
|
||||
|
||||
return Typeface.Default;
|
||||
}
|
||||
|
||||
public string GetFontPath(string name)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
if (GetFontStyleById(name) is NativeFontStyle fontStyle)
|
||||
return fontStyle.Path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ClearFontCache()
|
||||
{
|
||||
_fontFamilies = null;
|
||||
|
||||
foreach (var entry in _typeFaces)
|
||||
{
|
||||
try
|
||||
{
|
||||
entry.Value.Dispose();
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Info("Unable to dispose of a typeface", exc);
|
||||
}
|
||||
}
|
||||
|
||||
_typeFaces.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
using System.IO;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeFontStyle : IFontStyle, IComparable
|
||||
{
|
||||
private readonly NativeFontFamily _family;
|
||||
|
||||
public NativeFontStyle(NativeFontFamily family, string id, string name, string fullName, int weight, FontStyleType styleType, string path, bool resource = false)
|
||||
{
|
||||
_family = family;
|
||||
Id = id;
|
||||
Name = name;
|
||||
FullName = fullName;
|
||||
Weight = weight;
|
||||
StyleType = styleType;
|
||||
Path = path;
|
||||
Resource = resource;
|
||||
}
|
||||
|
||||
public IFontFamily FontFamily => _family;
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string FullName { get; }
|
||||
|
||||
public int Weight { get; }
|
||||
|
||||
public FontStyleType StyleType { get; }
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public bool Resource { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
if (obj.GetType() != typeof(NativeFontStyle))
|
||||
return false;
|
||||
NativeFontStyle other = (NativeFontStyle) obj;
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return Id != null ? Id.GetHashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public int CompareTo(IFontStyle other)
|
||||
{
|
||||
if (other == null)
|
||||
return -1;
|
||||
|
||||
if (Name.Equals("Regular") || Name.Equals("Plain"))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (other.Name.Equals("Regular") || other.Name.Equals("Plain"))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return String.Compare(Name, other.Name, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public Stream OpenStream()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return CompareTo(obj as IFontStyle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
using System.IO;
|
||||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeGraphicsService : IGraphicsService
|
||||
{
|
||||
public static readonly NativeGraphicsService Instance = new NativeGraphicsService();
|
||||
|
||||
private static string _systemFontName;
|
||||
private static string _boldSystemFontName;
|
||||
|
||||
public IImage LoadImageFromStream(Stream stream, ImageFormat formatHint = ImageFormat.Png)
|
||||
{
|
||||
var bitmap = BitmapFactory.DecodeStream(stream);
|
||||
return new NativeImage(bitmap);
|
||||
}
|
||||
|
||||
public string SystemFontName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_systemFontName == null)
|
||||
{
|
||||
_systemFontName = NativeFontService.SystemFont;
|
||||
_boldSystemFontName = NativeFontService.SystemBoldFont;
|
||||
}
|
||||
|
||||
return _systemFontName;
|
||||
}
|
||||
}
|
||||
|
||||
public string BoldSystemFontName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_boldSystemFontName == null)
|
||||
{
|
||||
_systemFontName = NativeFontService.SystemFont;
|
||||
_boldSystemFontName = NativeFontService.SystemBoldFont;
|
||||
}
|
||||
|
||||
return _boldSystemFontName;
|
||||
}
|
||||
}
|
||||
|
||||
public SizeF GetStringSize(string value, string fontName, float fontSize)
|
||||
{
|
||||
if (value == null) return new SizeF();
|
||||
|
||||
var textPaint = new TextPaint {TextSize = fontSize};
|
||||
textPaint.SetTypeface(NativeFontService.Instance.GetTypeface(fontName));
|
||||
|
||||
var staticLayout = TextLayoutUtils.CreateLayout(value, textPaint, null, Layout.Alignment.AlignNormal);
|
||||
var size = staticLayout.GetTextSizeAsEWSize(false);
|
||||
staticLayout.Dispose();
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SizeF GetStringSize(string aString, string aFontName, float aFontSize, HorizontalAlignment aHorizontalAlignment, VerticalAlignment aVerticalAlignment)
|
||||
{
|
||||
if (aString == null) return new SizeF();
|
||||
|
||||
var vTextPaint = new TextPaint {TextSize = aFontSize};
|
||||
vTextPaint.SetTypeface(NativeFontService.Instance.GetTypeface(aFontName));
|
||||
|
||||
Layout.Alignment vAlignment;
|
||||
switch (aHorizontalAlignment)
|
||||
{
|
||||
case HorizontalAlignment.Center:
|
||||
vAlignment = Layout.Alignment.AlignCenter;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
vAlignment = Layout.Alignment.AlignOpposite;
|
||||
break;
|
||||
default:
|
||||
vAlignment = Layout.Alignment.AlignNormal;
|
||||
break;
|
||||
}
|
||||
|
||||
StaticLayout vLayout = TextLayoutUtils.CreateLayout(aString, vTextPaint, null, vAlignment);
|
||||
SizeF vSize = vLayout.GetTextSizeAsEWSize(false);
|
||||
vLayout.Dispose();
|
||||
return vSize;
|
||||
}
|
||||
|
||||
public BitmapExportContext CreateBitmapExportContext(int width, int height, float displayScale = 1)
|
||||
{
|
||||
return new NativeBitmapExportContext(width, height, displayScale, 72, false, true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public class NativeImage : IImage
|
||||
{
|
||||
private Bitmap _bitmap;
|
||||
|
||||
public NativeImage(Bitmap bitmap)
|
||||
{
|
||||
_bitmap = bitmap;
|
||||
}
|
||||
|
||||
public float Width => _bitmap.Width;
|
||||
|
||||
public float Height => _bitmap.Height;
|
||||
|
||||
public IImage Downsize(float maxWidthOrHeight, bool disposeOriginal = false)
|
||||
{
|
||||
var downsizedImage = _bitmap.Downsize((int) maxWidthOrHeight, disposeOriginal);
|
||||
return new NativeImage(downsizedImage);
|
||||
}
|
||||
|
||||
public IImage Downsize(float maxWidth, float maxHeight, bool disposeOriginal = false)
|
||||
{
|
||||
var downsizedImage = _bitmap.Downsize((int) maxWidth, (int) maxHeight, disposeOriginal);
|
||||
return new NativeImage(downsizedImage);
|
||||
}
|
||||
|
||||
public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMode.Fit, bool disposeOriginal = false)
|
||||
{
|
||||
using (var context = new NativeBitmapExportContext((int) width, (int) height))
|
||||
{
|
||||
var fx = width / Width;
|
||||
var fy = height / Height;
|
||||
|
||||
var w = Width;
|
||||
var h = Height;
|
||||
|
||||
var x = 0f;
|
||||
var y = 0f;
|
||||
|
||||
if (resizeMode == ResizeMode.Fit)
|
||||
{
|
||||
if (fx < fy)
|
||||
{
|
||||
w *= fx;
|
||||
h *= fx;
|
||||
}
|
||||
else
|
||||
{
|
||||
w *= fy;
|
||||
h *= fy;
|
||||
}
|
||||
|
||||
x = (width - w) / 2;
|
||||
y = (height - h) / 2;
|
||||
}
|
||||
else if (resizeMode == ResizeMode.Bleed)
|
||||
{
|
||||
if (fx > fy)
|
||||
{
|
||||
w *= fx;
|
||||
h *= fx;
|
||||
}
|
||||
else
|
||||
{
|
||||
w *= fy;
|
||||
h *= fy;
|
||||
}
|
||||
|
||||
x = (width - w) / 2;
|
||||
y = (height - h) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
w = width;
|
||||
h = height;
|
||||
}
|
||||
|
||||
context.Canvas.DrawImage(this, x, y, w, h);
|
||||
return context.Image;
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap NativeRepresentation => _bitmap;
|
||||
|
||||
public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Jpeg:
|
||||
_bitmap.Compress(Bitmap.CompressFormat.Jpeg, (int) (quality * 100), stream);
|
||||
break;
|
||||
default:
|
||||
_bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Jpeg:
|
||||
await _bitmap.CompressAsync(Bitmap.CompressFormat.Jpeg, (int) (quality * 100), stream);
|
||||
break;
|
||||
default:
|
||||
await _bitmap.CompressAsync(Bitmap.CompressFormat.Png, 100, stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var disp = Interlocked.Exchange(ref _bitmap, null);
|
||||
disp?.Dispose();
|
||||
}
|
||||
|
||||
public void Draw(ICanvas canvas, RectangleF dirtyRect)
|
||||
{
|
||||
canvas.DrawImage(this, dirtyRect.Left, dirtyRect.Top, (float) Math.Round(dirtyRect.Width), (float) Math.Round(dirtyRect.Height));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.axml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.axml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called "R"
|
||||
(this is an Android convention) that contains the tokens for each one of the resources
|
||||
included. For example, for the above Resources layout, this is what the R class would expose:
|
||||
|
||||
public class R {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
|
||||
to reference the layout/main.axml file, or R.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
|
@ -0,0 +1,56 @@
|
|||
#pragma warning disable 1591
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[assembly: global::Android.Runtime.ResourceDesignerAttribute("System.Graphics.Android.Resource", IsApplication=false)]
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
|
||||
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
|
||||
public partial class Resource
|
||||
{
|
||||
|
||||
static Resource()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
public partial class Attribute
|
||||
{
|
||||
|
||||
static Attribute()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private Attribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class String
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7F010000
|
||||
public static int library_name = 2130771968;
|
||||
|
||||
static String()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private String()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="library_name">System.Graphics.Android</string>
|
||||
</resources>
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ProjectGuid>{F63D421B-A5E5-4107-9BAC-9C45ED1363DE}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>System.Graphics.Android</RootNamespace>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AssemblyName>System.Graphics.Android</AssemblyName>
|
||||
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
|
||||
<AndroidTlsProvider>
|
||||
</AndroidTlsProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Mono.Android" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FontInfo.cs" />
|
||||
<Compile Include="ImageExtensions.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="BufferedCanvas.cs" />
|
||||
<Compile Include="FontAnalyzer.cs" />
|
||||
<Compile Include="NativeCanvas.cs" />
|
||||
<Compile Include="NativeCanvasState.cs" />
|
||||
<Compile Include="DirectRenderer.cs" />
|
||||
<Compile Include="NativeBitmapExportContext.cs" />
|
||||
<Compile Include="IGraphicsRenderer.cs" />
|
||||
<Compile Include="NativeGraphicsService.cs" />
|
||||
<Compile Include="GraphicsView.cs" />
|
||||
<Compile Include="NativeImage.cs" />
|
||||
<Compile Include="TextLayoutUtils.cs" />
|
||||
<Compile Include="GraphicsExtensions.cs" />
|
||||
<Compile Include="NativeFontService.cs" />
|
||||
<Compile Include="NativeFontFamily.cs" />
|
||||
<Compile Include="NativeFontStyle.cs" />
|
||||
<Compile Include="Text\AttributedTextExtensions.cs" />
|
||||
<Compile Include="Text\ColorExtensions.cs" />
|
||||
<Compile Include="Text\SpannableExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values\Strings.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Fonts\Arimo-Bold.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Arimo-BoldItalic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Arimo-Italic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Arimo-Regular.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Cousine-Bold.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Cousine-BoldItalic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Cousine-Italic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Cousine-Regular.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Tinos-Bold.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Tinos-BoldItalic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Tinos-Italic.ttf" />
|
||||
<EmbeddedResource Include="Fonts\Tinos-Regular.ttf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreCursor-Bold.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreCursor-BoldItalic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreCursor-Italic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreCursor-Regular.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreHeros-Bold.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreHeros-BoldItalic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreHeros-Italic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreHeros-Regular.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreTermes-Bold.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreTermes-BoldItalic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreTermes-Italic.otf" />
|
||||
<EmbeddedResource Include="Fonts\TeXGyreTermes-Regular.otf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\System.Graphics.Core\System.Graphics.Core.csproj">
|
||||
<Project>{76f3031e-341e-43cc-bd92-b23f83495db7}</Project>
|
||||
<Name>System.Graphics.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,116 @@
|
|||
using System.Graphics.Text;
|
||||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
using Android.Text.Style;
|
||||
|
||||
namespace System.Graphics.Android.Text
|
||||
{
|
||||
public static class AttributedTextExtensions
|
||||
{
|
||||
public static SpannableStringBuilder AsSpannableStringBuilder(this IAttributedText target)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
var spannableString = new SpannableStringBuilder(target.Text);
|
||||
HandleRuns(target, spannableString);
|
||||
return spannableString;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SpannableString AsSpannableString(this IAttributedText target)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
var spannableString = new SpannableString(target.Text);
|
||||
HandleRuns(target, spannableString);
|
||||
return spannableString;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void HandleRuns(
|
||||
IAttributedText target,
|
||||
ISpannable spannableString)
|
||||
{
|
||||
foreach (var run in target.Runs)
|
||||
HandleFormatRun(spannableString, run);
|
||||
}
|
||||
|
||||
private static void HandleFormatRun(ISpannable spannableString, IAttributedTextRun run)
|
||||
{
|
||||
var attributes = run.Attributes;
|
||||
var start = run.Start;
|
||||
var end = start + run.Length;
|
||||
|
||||
var fontName = attributes.GetFontName();
|
||||
if (fontName != null)
|
||||
{
|
||||
var typefaceSpan = new TypefaceSpan(fontName);
|
||||
spannableString.SetSpan(typefaceSpan, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetBold() && attributes.GetItalic())
|
||||
{
|
||||
var span = new StyleSpan(TypefaceStyle.BoldItalic);
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
else if (attributes.GetBold())
|
||||
{
|
||||
var span = new StyleSpan(TypefaceStyle.Bold);
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
else if (attributes.GetItalic())
|
||||
{
|
||||
var span = new StyleSpan(TypefaceStyle.Italic);
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetUnderline())
|
||||
{
|
||||
var span = new UnderlineSpan();
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
var foregroundColor = attributes.GetForegroundColor()?.ParseAsInts()?.ToColor();
|
||||
if (foregroundColor != null)
|
||||
{
|
||||
var span = new ForegroundColorSpan((global::Android.Graphics.Color) foregroundColor);
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
var backgroundColor = attributes.GetBackgroundColor()?.ParseAsInts()?.ToColor();
|
||||
if (backgroundColor != null)
|
||||
{
|
||||
var span = new BackgroundColorSpan((global::Android.Graphics.Color) backgroundColor);
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetSubscript())
|
||||
{
|
||||
var span = new SubscriptSpan();
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetSuperscript())
|
||||
{
|
||||
var span = new SuperscriptSpan();
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetStrikethrough())
|
||||
{
|
||||
var span = new StrikethroughSpan();
|
||||
spannableString.SetSpan(span, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
|
||||
if (attributes.GetUnorderedList())
|
||||
{
|
||||
var bulletSpan = new BulletSpan();
|
||||
spannableString.SetSpan(bulletSpan, start, end, SpanTypes.ExclusiveExclusive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using Android.Graphics;
|
||||
|
||||
namespace System.Graphics.Android.Text
|
||||
{
|
||||
public static class ColorExtensions
|
||||
{
|
||||
public static global::Android.Graphics.Color? ToColor(this int[] color)
|
||||
{
|
||||
if (color == null)
|
||||
return null;
|
||||
|
||||
return new global::Android.Graphics.Color(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Graphics.Text.Immutable;
|
||||
using System.IO;
|
||||
using Android.Text;
|
||||
|
||||
namespace System.Graphics.Android.Text
|
||||
{
|
||||
public static class SpannableExtensions
|
||||
{
|
||||
public static AttributedText AsAttributedText(this ISpannable target)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
using (var textWriter = new StringWriter())
|
||||
{
|
||||
var runs = CreateRuns(target, textWriter);
|
||||
return new AttributedText(textWriter.ToString(), runs);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<AttributedTextRun> CreateRuns(
|
||||
ISpannable target,
|
||||
TextWriter writer)
|
||||
{
|
||||
var runs = new List<AttributedTextRun>();
|
||||
|
||||
var spans = target.GetSpans(0, target.Length(), null);
|
||||
if (spans != null)
|
||||
{
|
||||
foreach (var span in spans)
|
||||
{
|
||||
System.Console.WriteLine(span);
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write(target.ToString());
|
||||
return runs;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using System.Drawing;
|
||||
using Android.Text;
|
||||
|
||||
namespace System.Graphics.Android
|
||||
{
|
||||
public static class TextLayoutUtils
|
||||
{
|
||||
public static StaticLayout CreateLayout(string text, TextPaint textPaint, int? boundedWidth, Layout.Alignment alignment)
|
||||
{
|
||||
int finalWidth = int.MaxValue;
|
||||
if (boundedWidth > 0)
|
||||
finalWidth = (int) boundedWidth;
|
||||
|
||||
var layout = new StaticLayout(
|
||||
text, // Text to layout
|
||||
textPaint, // Text paint (font, size, etc...) to use
|
||||
finalWidth, // The maximum width the text can be
|
||||
alignment, // The horizontal alignment of the text
|
||||
1.0f, // Spacing multiplier
|
||||
0.0f, // Additional spacing
|
||||
false); // Include padding
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static StaticLayout CreateLayoutForSpannedString(SpannableString spannedString, TextPaint textPaint, int? boundedWidth, Layout.Alignment alignment)
|
||||
{
|
||||
int finalWidth = int.MaxValue;
|
||||
if (boundedWidth > 0)
|
||||
finalWidth = (int) boundedWidth;
|
||||
|
||||
var layout = new StaticLayout(
|
||||
spannedString, // Text to layout
|
||||
textPaint, // Text paint (font, size, etc...) to use
|
||||
finalWidth, // The maximum width the text can be
|
||||
alignment, // The horizontal alignment of the text
|
||||
1.0f, // Spacing multiplier
|
||||
0.0f, // Additional spacing
|
||||
false); // Include padding
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static Drawing.SizeF GetTextSize(this StaticLayout target)
|
||||
{
|
||||
// Get the text bounds and assume (the safe assumption) that the layout wasn't
|
||||
// created with a bounded width.
|
||||
return GetTextSize(target, false);
|
||||
}
|
||||
|
||||
public static Drawing.SizeF GetTextSize(this StaticLayout target, bool hasBoundedWidth)
|
||||
{
|
||||
// We need to know if the static layout was created with a bounded width, as this is what
|
||||
// StaticLayout.Width returns.
|
||||
if (hasBoundedWidth)
|
||||
return new Drawing.SizeF(target.Width, target.Height);
|
||||
|
||||
float vMaxWidth = 0;
|
||||
int vLineCount = target.LineCount;
|
||||
|
||||
for (int i = 0; i < vLineCount; i++)
|
||||
{
|
||||
float vLineWidth = target.GetLineWidth(i);
|
||||
if (vLineWidth > vMaxWidth)
|
||||
vMaxWidth = vLineWidth;
|
||||
}
|
||||
|
||||
return new Drawing.SizeF(vMaxWidth, target.Height);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<config>
|
||||
<add key="repositoryPath" value="../../../../packages" />
|
||||
</config>
|
||||
</configuration>
|
|
@ -0,0 +1,35 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
|
||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
||||
<DefaultItemExcludes>${DefaultItemExcludes};dist\**;node_modules\**</DefaultItemExcludes>
|
||||
<NoWarn>CS2008</NoWarn>
|
||||
<!-- VS's FastUpToDateCheck doesn't consider .ts file changes, so it's necessary to disable it to get incremental builds to work correctly (albeit not as fast as if FastUpToDateCheck did work for them) -->
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>2.0</OldToolsVersion>
|
||||
<RootNamespace>System.Graphics.Blazor.JS</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.9.0-preview3-19154-02" />
|
||||
<WebpackInputs Include="**\*.ts" Exclude="dist\**;node_modules\**" />
|
||||
</ItemGroup>
|
||||
<Target Name="EnsureNpmRestored" Condition="!Exists('node_modules')">
|
||||
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
|
||||
<Exec Command="npm install" />
|
||||
</Target>
|
||||
<Target Name="RunWebpack" AfterTargets="ResolveReferences" Inputs="@(WebpackInputs)" Outputs="dist\blazor.extensions.canvas.js" DependsOnTargets="EnsureNpmRestored">
|
||||
<RemoveDir Directories="dist" />
|
||||
<Exec Command="npm run build" />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="dist\**\*.js" LogicalName="blazor:js:%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "blazor.extensions.canvas",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack-cli",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "0.0.31",
|
||||
"ts-loader": "^4.4.2",
|
||||
"typescript": "^2.9.2",
|
||||
"webpack": "^4.16.3",
|
||||
"webpack-cli": "^3.1.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
export class ContextManager {
|
||||
private readonly contexts = new Map<string, any>();
|
||||
private readonly webGLObject = new Array<any>();
|
||||
private readonly contextName: string;
|
||||
private webGLContext = false;
|
||||
private readonly prototypes: any;
|
||||
private readonly webGLTypes = [
|
||||
WebGLBuffer, WebGLShader, WebGLProgram, WebGLFramebuffer, WebGLRenderbuffer, WebGLTexture, WebGLUniformLocation
|
||||
];
|
||||
|
||||
public constructor(contextName: string) {
|
||||
this.contextName = contextName;
|
||||
if (contextName === "2d")
|
||||
this.prototypes = CanvasRenderingContext2D.prototype;
|
||||
else if (contextName === "webgl" || contextName === "experimental-webgl") {
|
||||
this.prototypes = WebGLRenderingContext.prototype;
|
||||
this.webGLContext = true;
|
||||
} else
|
||||
throw new Error(`Invalid context name: ${contextName}`);
|
||||
}
|
||||
|
||||
public add = (canvas: HTMLCanvasElement, parameters: any) => {
|
||||
if (!canvas) throw new Error('Invalid canvas.');
|
||||
if (this.contexts.get(canvas.id)) return;
|
||||
|
||||
var context;
|
||||
if (parameters)
|
||||
context = canvas.getContext(this.contextName, parameters);
|
||||
else
|
||||
context = canvas.getContext(this.contextName);
|
||||
|
||||
if (!context) throw new Error('Invalid context.');
|
||||
|
||||
this.contexts.set(canvas.id, context);
|
||||
}
|
||||
|
||||
public remove = (canvas: HTMLCanvasElement) => {
|
||||
this.contexts.delete(canvas.id);
|
||||
}
|
||||
|
||||
public setProperty = (canvas: HTMLCanvasElement, property: string, value: any) => {
|
||||
const context = this.getContext(canvas);
|
||||
context[property] = this.deserialize(property, value);
|
||||
}
|
||||
|
||||
public getProperty = (canvas: HTMLCanvasElement, property: string) => {
|
||||
const context = this.getContext(canvas);
|
||||
return this.serialize(context[property]);
|
||||
}
|
||||
|
||||
public call = (canvas: HTMLCanvasElement, method: string, args: any) => {
|
||||
const context = this.getContext(canvas);
|
||||
return this.serialize(this.prototypes[method].apply(context, args != undefined ? args.map((value) => this.deserialize(method, value)) : []));
|
||||
}
|
||||
|
||||
private getContext = (canvas: HTMLCanvasElement) => {
|
||||
if (!canvas) throw new Error('Invalid canvas.');
|
||||
|
||||
const context = this.contexts.get(canvas.id);
|
||||
if (!context) throw new Error('Invalid context.');
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private deserialize = (method: string, object: any) => {
|
||||
if (!this.webGLContext) return object; //deserialization only needs to happen for webGL
|
||||
|
||||
if (object.hasOwnProperty("webGLType") && object.hasOwnProperty("id")) {
|
||||
return (this.webGLObject[object["id"]]);
|
||||
} else if (Array.isArray(object) && !method.endsWith("v")) {
|
||||
return Int8Array.of(...(object as number[]));
|
||||
} else
|
||||
return object;
|
||||
}
|
||||
|
||||
private serialize = (object: any) => {
|
||||
if (!this.webGLContext) return object; //serialization only needs to happen for webGL
|
||||
|
||||
const type = this.webGLTypes.find((type) => object instanceof type);
|
||||
if (type != undefined) {
|
||||
const id = this.webGLObject.length;
|
||||
this.webGLObject.push(object);
|
||||
|
||||
return {
|
||||
webGLType: type.name,
|
||||
id: id
|
||||
};
|
||||
} else
|
||||
return object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { ContextManager } from './CanvasContextManager';
|
||||
|
||||
namespace Canvas {
|
||||
const blazorExtensions: string = 'BlazorExtensions';
|
||||
// define what this extension adds to the window object inside BlazorExtensions
|
||||
const extensionObject = {
|
||||
Canvas2d: new ContextManager("2d"),
|
||||
WebGL: new ContextManager("webgl")
|
||||
};
|
||||
|
||||
export function initialize(): void {
|
||||
if (typeof window !== 'undefined' && !window[blazorExtensions]) {
|
||||
// when the library is loaded in a browser via a <script> element, make the
|
||||
// following APIs available in global scope for invocation from JS
|
||||
window[blazorExtensions] = {
|
||||
...extensionObject
|
||||
};
|
||||
} else {
|
||||
window[blazorExtensions] = {
|
||||
...window[blazorExtensions],
|
||||
...extensionObject
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Canvas.initialize();
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"noEmitOnError": true,
|
||||
"removeComments": true,
|
||||
"sourceMap": true,
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"lib": ["es2016", "dom"],
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"preserveConstEnums": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"tabSize": 2,
|
||||
"indentSize": 2,
|
||||
"convertTabsToSpaces": true,
|
||||
"insertSpaceAfterCommaDelimiter": true,
|
||||
"insertSpaceAfterSemicolonInForStatements": true,
|
||||
"insertSpaceBeforeAndAfterBinaryOperators": true,
|
||||
"insertSpaceAfterKeywordsInControlFlowStatements": true,
|
||||
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
|
||||
"insertSpaceBeforeFunctionParenthesis": false,
|
||||
"placeOpenBraceOnNewLineForFunctions": false,
|
||||
"placeOpenBraceOnNewLineForControlBlocks": false
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"rules": {
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-duplicate-variable": true,
|
||||
"curly": true,
|
||||
"class-name": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"]
|
||||
},
|
||||
devtool: "inline-source-map",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts?$/,
|
||||
loader: "ts-loader"
|
||||
}
|
||||
]
|
||||
},
|
||||
entry: {
|
||||
"blazor.extensions.canvas": "./src/InitializeCanvas.ts"
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, "/dist"),
|
||||
filename: "[name].js"
|
||||
}
|
||||
};
|
|
@ -0,0 +1,466 @@
|
|||
using System.Graphics.Blazor.Canvas2D;
|
||||
using System.Graphics.Text;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public class BlazorCanvas : AbstractCanvas<BlazorCanvasState>
|
||||
{
|
||||
private readonly float[] _matrix = new float[6];
|
||||
private RectangleF _bounds;
|
||||
private CanvasRenderingContext2D _context;
|
||||
|
||||
public BlazorCanvas() : base(CreateNewState, CreateStateCopy)
|
||||
{
|
||||
}
|
||||
|
||||
private static BlazorCanvasState CreateNewState(object context)
|
||||
{
|
||||
return new BlazorCanvasState();
|
||||
}
|
||||
|
||||
private static BlazorCanvasState CreateStateCopy(BlazorCanvasState prototype)
|
||||
{
|
||||
return new BlazorCanvasState(prototype);
|
||||
}
|
||||
|
||||
public CanvasRenderingContext2D Context
|
||||
{
|
||||
get => _context;
|
||||
set => _context = value;
|
||||
}
|
||||
|
||||
public override float MiterLimit { set => CurrentState.MiterLimit = value; }
|
||||
public override Color StrokeColor { set => CurrentState.StrokeColor = value; }
|
||||
public override LineCap StrokeLineCap { set => CurrentState.LineCap = value; }
|
||||
public override LineJoin StrokeLineJoin { set => CurrentState.LineJoin = value; }
|
||||
public override Color FillColor { set => CurrentState.FillColor = value; }
|
||||
public override Color FontColor { set => CurrentState.TextColor = value; }
|
||||
public override string FontName { set => CurrentState.Font = value; }
|
||||
public override float FontSize { set => CurrentState.FontSize = value; }
|
||||
public override float Alpha { set => _context.GlobalAlpha = value; }
|
||||
public override bool Antialias { set { /* do nothing */} }
|
||||
public override BlendMode BlendMode { set { /* do nothing */ } }
|
||||
|
||||
public void ClearRect(float x1, float y1, float width, float height)
|
||||
{
|
||||
_context.ClearRect(x1, y1, width, height);
|
||||
_bounds = new RectangleF(x1, y1, width, height);
|
||||
}
|
||||
|
||||
protected override float NativeStrokeSize { set => CurrentState.LineWidth = value; }
|
||||
|
||||
public override void ClipPath(PathF path, WindingMode windingMode = WindingMode.NonZero)
|
||||
{
|
||||
AddPath(path,1,1);
|
||||
_context.Clip(windingMode == WindingMode.NonZero ? "nonzero" : "evenodd");
|
||||
}
|
||||
|
||||
public override void ClipRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
_context.Rect(x,y,width,height);
|
||||
_context.Clip();
|
||||
}
|
||||
|
||||
public override void DrawImage(IImage image, float x, float y, float width, float height)
|
||||
{
|
||||
Logger.Debug("BlazorCanvas.DrawImage - not yet supported.");
|
||||
}
|
||||
|
||||
private SizeF GetTextSize(TextMetrics metrics, string value)
|
||||
{
|
||||
var lines = value.Split('\n');
|
||||
|
||||
var w = metrics.ActualBoundingBoxRight - metrics.ActualBoundingBoxLeft;
|
||||
var h = lines.Length * (metrics.ActualBoundingBoxAscent + metrics.ActualBoundingBoxDescent);
|
||||
|
||||
return new SizeF((float)w,(float)h);
|
||||
}
|
||||
|
||||
public override void DrawString(
|
||||
string value,
|
||||
float x,
|
||||
float y,
|
||||
HorizontalAlignment horizontalAlignment)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return;
|
||||
|
||||
var alpha = CurrentState.SetTextStyle(_context);
|
||||
var metrics = _context.MeasureText(value);
|
||||
|
||||
y += (float)(metrics.FontBoundingBoxAscent + metrics.FontBoundingBoxDescent);
|
||||
|
||||
if (horizontalAlignment == HorizontalAlignment.Right)
|
||||
{
|
||||
x -= (float)metrics.Width;
|
||||
}
|
||||
else if (horizontalAlignment == HorizontalAlignment.Center)
|
||||
{
|
||||
x -= (float)(metrics.Width / 2);
|
||||
}
|
||||
|
||||
_context.FillText(value, x, y);
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void DrawString(
|
||||
string value,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
HorizontalAlignment horizontalAlignment,
|
||||
VerticalAlignment verticalAlignment,
|
||||
TextFlow textFlow = TextFlow.ClipBounds,
|
||||
float lineSpacingAdjustment = 0)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return;
|
||||
|
||||
var alpha = CurrentState.SetTextStyle(_context);
|
||||
var metrics = _context.MeasureText(value);
|
||||
var size = GetTextSize(metrics, value);
|
||||
|
||||
float dx = 0;
|
||||
float dy = 0;
|
||||
|
||||
if (horizontalAlignment == HorizontalAlignment.Center)
|
||||
{
|
||||
var diff = width - size.Width;
|
||||
dx = diff / 4;
|
||||
}
|
||||
else if (horizontalAlignment == HorizontalAlignment.Right)
|
||||
{
|
||||
var diff = width - size.Width;
|
||||
dx = diff / 2;
|
||||
}
|
||||
|
||||
if (verticalAlignment == VerticalAlignment.Top)
|
||||
dy = CurrentState.FontSize * .8f;
|
||||
else if (verticalAlignment == VerticalAlignment.Center)
|
||||
dy = ((height - size.Height) / 2) + (CurrentState.FontSize *.6f / 2);
|
||||
else if (verticalAlignment == VerticalAlignment.Bottom)
|
||||
dy = height - size.Height - CurrentState.FontSize * .3f;
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
_context.FillText(value, x, y, width);
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void DrawText(IAttributedText value, float x, float y, float width, float height)
|
||||
{
|
||||
Logger.Debug("BlazorCanvas.DrawText - not yet supported.");
|
||||
}
|
||||
|
||||
public override void FillArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise)
|
||||
{
|
||||
var alpha = CurrentState.SetFillStyle(_context);
|
||||
_context.BeginPath();
|
||||
_context.Arc(x, y, width / 2, startAngle, endAngle, !clockwise);
|
||||
_context.Fill();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void FillOval(float x, float y, float width, float height)
|
||||
{
|
||||
var alpha = CurrentState.SetFillStyle(_context);
|
||||
_context.BeginPath();
|
||||
_context.Ellipse(x + (width / 2), y + (height / 2), width / 2, height / 2, 0, 0, 2 * Math.PI, true);
|
||||
_context.Fill();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void FillPath(PathF path, WindingMode windingMode)
|
||||
{
|
||||
var alpha = CurrentState.SetFillStyle(_context);
|
||||
_context.BeginPath();
|
||||
AddPath(path,1,1);
|
||||
_context.Fill();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void FillRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
var alpha = CurrentState.SetFillStyle(_context);
|
||||
_context.FillRect(x, y, width, height);
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void FillRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
|
||||
{
|
||||
var alpha = CurrentState.SetFillStyle(_context);
|
||||
_context.BeginPath();
|
||||
AddRoundedRectangle(x, y, width, height, cornerRadius);
|
||||
_context.Fill();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
public override void SetFillPaint(Paint paint, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
if (paint.PaintType == PaintType.Solid)
|
||||
{
|
||||
FillColor = paint.StartColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentState.SetFillPaint(paint, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetShadow(SizeF offset, float blur, Color color)
|
||||
{
|
||||
Logger.Debug("BlazorCanvas.SetShadow - not yet supported.");
|
||||
}
|
||||
|
||||
public override void SetToBoldSystemFont()
|
||||
{
|
||||
FontName = "Arial";
|
||||
}
|
||||
|
||||
public override void SetToSystemFont()
|
||||
{
|
||||
FontName = "Arial-Bold";
|
||||
}
|
||||
|
||||
public override void SubtractFromClip(float x, float y, float width, float height)
|
||||
{
|
||||
_context.Rect(_bounds.X, _bounds.Y, _bounds.Width, _bounds.Width);
|
||||
_context.Rect(x,y,width,height);
|
||||
_context.Clip("evenodd");
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
transform.GetMatrix(_matrix);
|
||||
_context.SetTransform(_matrix[0], _matrix[1], _matrix[2], _matrix[3], _matrix[4], _matrix[5]);
|
||||
}
|
||||
|
||||
protected override void NativeDrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
_context.BeginPath();
|
||||
_context.Arc(x, y, width / 2, startAngle, endAngle, !clockwise);
|
||||
_context.Stroke();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeDrawLine(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
|
||||
_context.BeginPath();
|
||||
_context.MoveTo(x1, y1);
|
||||
_context.LineTo(x2, y2);
|
||||
_context.Stroke();
|
||||
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeDrawOval(float x, float y, float width, float height)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
|
||||
_context.BeginPath();
|
||||
_context.Ellipse(x + (width / 2), y + (height / 2), width / 2, height / 2, 0, 0, 2 * Math.PI, true);
|
||||
_context.Stroke();
|
||||
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeDrawPath(PathF path)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
_context.BeginPath();
|
||||
AddPath(path, 1, 1);
|
||||
_context.Stroke();
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeDrawRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
_context.StrokeRect(x, y, width, height);
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeDrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
|
||||
{
|
||||
var alpha = CurrentState.SetStrokeStyle(_context);
|
||||
|
||||
_context.BeginPath();
|
||||
AddRoundedRectangle(x, y, width, height, cornerRadius);
|
||||
_context.Stroke();
|
||||
|
||||
_context.GlobalAlpha = alpha;
|
||||
}
|
||||
|
||||
protected override void NativeRotate(float degrees, float radians, float x, float y)
|
||||
{
|
||||
_context.Translate(x, y);
|
||||
_context.Rotate(radians);
|
||||
_context.Translate(-x, -y);
|
||||
}
|
||||
|
||||
protected override void NativeRotate(float degrees, float radians)
|
||||
{
|
||||
_context.Rotate(radians);
|
||||
}
|
||||
|
||||
protected override void NativeScale(float fx, float fy)
|
||||
{
|
||||
_context.Scale(fx, fy);
|
||||
}
|
||||
|
||||
protected override void NativeSetStrokeDashPattern(float[] pattern, float strokeSize)
|
||||
{
|
||||
float[] finalPattern = null;
|
||||
if (pattern != null)
|
||||
{
|
||||
finalPattern = new float[pattern.Length];
|
||||
for (int i = 0; i < pattern.Length; i++)
|
||||
finalPattern[i] = pattern[i] * strokeSize;
|
||||
}
|
||||
|
||||
CurrentState.BlazorDashPattern = finalPattern;
|
||||
}
|
||||
|
||||
protected override void NativeTranslate(float tx, float ty)
|
||||
{
|
||||
_context.Translate(tx, ty);
|
||||
}
|
||||
|
||||
public override void ResetState()
|
||||
{
|
||||
base.ResetState();
|
||||
_context.ResetTransform();
|
||||
}
|
||||
|
||||
public override bool RestoreState()
|
||||
{
|
||||
var result = base.RestoreState();
|
||||
if (result)
|
||||
{
|
||||
_context.Restore();
|
||||
CurrentState.Restore();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void SaveState()
|
||||
{
|
||||
base.SaveState();
|
||||
_context.Save();
|
||||
}
|
||||
|
||||
public void AddRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
|
||||
{
|
||||
var finalCornerRadius = cornerRadius;
|
||||
|
||||
var rect = new RectangleF(x, y, width, height);
|
||||
|
||||
if (finalCornerRadius > rect.Width)
|
||||
{
|
||||
finalCornerRadius = rect.Width / 2;
|
||||
}
|
||||
|
||||
if (finalCornerRadius > rect.Height)
|
||||
{
|
||||
finalCornerRadius = rect.Height / 2;
|
||||
}
|
||||
|
||||
var minx = rect.X;
|
||||
var miny = rect.Y;
|
||||
var maxx = minx + rect.Width;
|
||||
var maxy = miny + rect.Height;
|
||||
var midx = minx + (rect.Width / 2);
|
||||
var midy = miny + (rect.Height / 2);
|
||||
|
||||
_context.MoveTo(minx, midy);
|
||||
_context.ArcTo(minx, miny, midx, miny, finalCornerRadius);
|
||||
_context.ArcTo(maxx, miny, maxx, midy, finalCornerRadius);
|
||||
_context.ArcTo(maxx, maxy, midx, maxy, finalCornerRadius);
|
||||
_context.ArcTo(minx, maxy, minx, midy, finalCornerRadius);
|
||||
_context.ClosePath();
|
||||
}
|
||||
|
||||
private void AddPath(PathF target, float scaleX, float scaleY)
|
||||
{
|
||||
int pointIndex = 0;
|
||||
int arcAngleIndex = 0;
|
||||
int arcClockwiseIndex = 0;
|
||||
|
||||
foreach (var type in target.SegmentTypes)
|
||||
{
|
||||
if (type == PathOperation.Move)
|
||||
{
|
||||
var point = target[pointIndex++];
|
||||
_context.MoveTo((point.X * scaleX), (point.Y * scaleY));
|
||||
}
|
||||
else if (type == PathOperation.Line)
|
||||
{
|
||||
var endPoint = target[pointIndex++];
|
||||
_context.LineTo((endPoint.X * scaleX), (endPoint.Y * scaleY));
|
||||
}
|
||||
|
||||
else if (type == PathOperation.Quad)
|
||||
{
|
||||
var controlPoint = target[pointIndex++];
|
||||
var endPoint = target[pointIndex++];
|
||||
_context.QuadraticCurveTo(
|
||||
(controlPoint.X * scaleX), (controlPoint.Y * scaleY),
|
||||
(endPoint.X * scaleX), (endPoint.Y * scaleY));
|
||||
}
|
||||
else if (type == PathOperation.Cubic)
|
||||
{
|
||||
var controlPoint1 = target[pointIndex++];
|
||||
var controlPoint2 = target[pointIndex++];
|
||||
var endPoint = target[pointIndex++];
|
||||
_context.BezierCurveTo(
|
||||
(controlPoint1.X * scaleX), (controlPoint1.Y * scaleY),
|
||||
(controlPoint2.X * scaleX), (controlPoint2.Y * scaleY),
|
||||
(endPoint.X * scaleX), (endPoint.Y * scaleY));
|
||||
}
|
||||
else if (type == PathOperation.Arc)
|
||||
{
|
||||
var topLeft = target[pointIndex++];
|
||||
var bottomRight = target[pointIndex++];
|
||||
float startAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
float endAngle = target.GetArcAngle(arcAngleIndex++);
|
||||
var clockwise = target.GetArcClockwise(arcClockwiseIndex++);
|
||||
|
||||
var startAngleInRadians = Geometry.DegreesToRadians(-startAngle);
|
||||
var endAngleInRadians = Geometry.DegreesToRadians(-endAngle);
|
||||
|
||||
while (startAngleInRadians < 0)
|
||||
{
|
||||
startAngleInRadians += (float)Math.PI * 2;
|
||||
}
|
||||
|
||||
while (endAngleInRadians < 0)
|
||||
{
|
||||
endAngleInRadians += (float)Math.PI * 2;
|
||||
}
|
||||
|
||||
var cx = (bottomRight.X + topLeft.X) / 2;
|
||||
var cy = (bottomRight.Y + topLeft.Y) / 2;
|
||||
var width = bottomRight.X - topLeft.X;
|
||||
var height = bottomRight.Y - topLeft.Y;
|
||||
var r = width / 2;
|
||||
|
||||
/*var transform = CGAffineTransform.MakeTranslation(ox + cx * ppu, oy + cy * ppu);
|
||||
transform = CGAffineTransform.Multiply(CGAffineTransform.MakeScale(1, height / width), transform);
|
||||
*/
|
||||
|
||||
_context.Arc(cx, cy, r, startAngle, endAngle, !clockwise);
|
||||
}
|
||||
else if (type == PathOperation.Close)
|
||||
{
|
||||
_context.ClosePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System.Graphics.Blazor.Canvas2D;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public static class BlazorCanvasExtensions
|
||||
{
|
||||
public static Canvas2D.LineCap AsCanvasValue(
|
||||
this LineCap target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case LineCap.Butt:
|
||||
return Canvas2D.LineCap.Butt;
|
||||
case LineCap.Round:
|
||||
return Canvas2D.LineCap.Round;
|
||||
case LineCap.Square:
|
||||
return Canvas2D.LineCap.Square;
|
||||
}
|
||||
|
||||
return Canvas2D.LineCap.Butt;
|
||||
}
|
||||
|
||||
public static Canvas2D.LineJoin AsCanvasValue(
|
||||
this LineJoin target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case LineJoin.Miter:
|
||||
return Canvas2D.LineJoin.Miter;
|
||||
case LineJoin.Round:
|
||||
return Canvas2D.LineJoin.Round;
|
||||
case LineJoin.Bevel:
|
||||
return Canvas2D.LineJoin.Bevel;
|
||||
}
|
||||
|
||||
return Canvas2D.LineJoin.Miter;
|
||||
}
|
||||
|
||||
public static string AsCanvasValue(
|
||||
this Color color,
|
||||
string defaultValue = "black")
|
||||
{
|
||||
if (color != null)
|
||||
return color.ToHexString();
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
using System.Graphics.Blazor.Canvas2D;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public class BlazorCanvasState : CanvasState
|
||||
{
|
||||
private static readonly float[] EmptyFloatArray = new float[] { };
|
||||
|
||||
private bool _strokeDirty = true;
|
||||
private float _miterLimit = CanvasDefaults.DefaultMiterLimit;
|
||||
private Color _strokeColor = Colors.Black;
|
||||
private float _lineWidth = 1;
|
||||
private LineJoin _lineJoin;
|
||||
private LineCap _lineCap;
|
||||
private float _alpha = 1;
|
||||
private float[] _dashPattern;
|
||||
|
||||
private bool _fillDirty = true;
|
||||
private Color _fillColor = Colors.White;
|
||||
private Paint _fillPaint = null;
|
||||
private double _fillX1;
|
||||
private double _fillY1;
|
||||
private double _fillX2;
|
||||
private double _fillY2;
|
||||
|
||||
private bool _textDirty = true;
|
||||
private Color _textColor = Colors.Black;
|
||||
private string _font = "Arial";
|
||||
private float _fontSize = 12f;
|
||||
|
||||
public BlazorCanvasState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BlazorCanvasState(BlazorCanvasState prototype) : base(prototype)
|
||||
{
|
||||
_strokeDirty = true;
|
||||
_strokeColor = prototype._strokeColor;
|
||||
_lineWidth = prototype._lineWidth;
|
||||
_lineJoin = prototype._lineJoin;
|
||||
_lineCap = prototype._lineCap;
|
||||
_miterLimit = prototype._miterLimit;
|
||||
|
||||
_fillDirty = true;
|
||||
_fillColor = prototype._fillColor;
|
||||
_fillPaint = prototype._fillPaint;
|
||||
_fillX1 = prototype._fillX1;
|
||||
_fillY1 = prototype._fillY1;
|
||||
_fillX2 = prototype._fillX2;
|
||||
_fillY2 = prototype._fillY2;
|
||||
_dashPattern = prototype._dashPattern;
|
||||
|
||||
_textDirty = true;
|
||||
_fontSize = prototype._fontSize;
|
||||
_textColor = prototype._textColor;
|
||||
_font = prototype._font;
|
||||
}
|
||||
|
||||
public Color StrokeColor
|
||||
{
|
||||
get => _strokeColor;
|
||||
set
|
||||
{
|
||||
if (_strokeColor != value)
|
||||
{
|
||||
_strokeColor = value ?? Colors.Black;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float MiterLimit
|
||||
{
|
||||
get => _miterLimit;
|
||||
set
|
||||
{
|
||||
if (_miterLimit != value)
|
||||
{
|
||||
_miterLimit = value;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LineJoin LineJoin
|
||||
{
|
||||
get => _lineJoin;
|
||||
set
|
||||
{
|
||||
if (_lineJoin != value)
|
||||
{
|
||||
_lineJoin = value;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LineCap LineCap
|
||||
{
|
||||
get => _lineCap;
|
||||
set
|
||||
{
|
||||
if (_lineCap != value)
|
||||
{
|
||||
_lineCap = value;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float LineWidth
|
||||
{
|
||||
get => _lineWidth;
|
||||
set
|
||||
{
|
||||
if (_lineWidth != value)
|
||||
{
|
||||
_lineWidth = value;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float[] BlazorDashPattern
|
||||
{
|
||||
get => _dashPattern;
|
||||
set
|
||||
{
|
||||
_dashPattern = value;
|
||||
_strokeDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Color FillColor
|
||||
{
|
||||
get => _fillColor;
|
||||
set
|
||||
{
|
||||
if (_fillColor != value)
|
||||
{
|
||||
_fillPaint = null;
|
||||
_fillColor = value ?? Colors.White;
|
||||
_fillDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get => _textColor;
|
||||
set
|
||||
{
|
||||
if (_textColor != value)
|
||||
{
|
||||
_textColor = value ?? Colors.Black;
|
||||
_textDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Alpha
|
||||
{
|
||||
get => _alpha;
|
||||
set => _alpha = value;
|
||||
}
|
||||
|
||||
public string Font
|
||||
{
|
||||
get => _font;
|
||||
set
|
||||
{
|
||||
if (!string.Equals(_font,value))
|
||||
{
|
||||
_font = value ?? "Arial";
|
||||
_textDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float FontSize
|
||||
{
|
||||
get => _fontSize;
|
||||
set
|
||||
{
|
||||
if (_fontSize != value)
|
||||
{
|
||||
_fontSize = value;
|
||||
_textDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float SetStrokeStyle(CanvasRenderingContext2D context)
|
||||
{
|
||||
context.GlobalAlpha = _strokeColor.Alpha * _alpha;
|
||||
|
||||
if (_strokeDirty)
|
||||
{
|
||||
context.StrokeStyle = _strokeColor.AsCanvasValue();
|
||||
context.LineWidth = _lineWidth;
|
||||
context.LineCap = _lineCap.AsCanvasValue();
|
||||
context.LineJoin = _lineJoin.AsCanvasValue();
|
||||
context.SetLineDash(_dashPattern ?? EmptyFloatArray);
|
||||
_strokeDirty = false;
|
||||
}
|
||||
|
||||
return _alpha;
|
||||
}
|
||||
|
||||
public float SetFillStyle(CanvasRenderingContext2D context)
|
||||
{
|
||||
if (_fillDirty)
|
||||
{
|
||||
if (_fillColor != null)
|
||||
{
|
||||
context.GlobalAlpha = _fillColor.Alpha * _alpha;
|
||||
context.FillStyle = _fillColor.AsCanvasValue();
|
||||
}
|
||||
else if (_fillPaint != null)
|
||||
{
|
||||
/*
|
||||
if (_fillPaint.PaintType == EWPaintType.LINEAR_GRADIENT)
|
||||
{
|
||||
var gradient = context.CreateLinearGradient(_fillX1, _fillY1, _fillX2, _fillY2);
|
||||
for (int i = 0; i < _fillPaint.Stops.Length; i++)
|
||||
{
|
||||
var color = _fillPaint.Stops[i].Color;
|
||||
var offset = _fillPaint.Stops[i].Offset;
|
||||
//var oouiColor = color != null ? color.AsOouiColorObject() : new Color(255, 255, 255, 255);
|
||||
var oouiColor = color != null ? color.AsOouiColor() : StandardColors.White.AsOouiColor();
|
||||
gradient.AddColorStop(offset, oouiColor);
|
||||
}
|
||||
context.FillStyle = gradient;
|
||||
}
|
||||
else if (_fillPaint.PaintType == EWPaintType.RADIAL_GRADIENT)
|
||||
{
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
_fillDirty = false;
|
||||
}
|
||||
|
||||
return _alpha;
|
||||
}
|
||||
|
||||
public float SetTextStyle(CanvasRenderingContext2D context)
|
||||
{
|
||||
context.GlobalAlpha = _textColor.Alpha * _alpha;
|
||||
|
||||
if (_textDirty)
|
||||
{
|
||||
context.Font = $"{_fontSize}px {_font}";
|
||||
context.FillStyle = _textColor.AsCanvasValue("black");
|
||||
_textDirty = false;
|
||||
}
|
||||
|
||||
return _alpha;
|
||||
}
|
||||
|
||||
internal void Restore()
|
||||
{
|
||||
_fillDirty = true;
|
||||
_strokeDirty = true;
|
||||
_textDirty = true;
|
||||
}
|
||||
|
||||
internal void SetFillPaint(Paint paint, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
_fillColor = null;
|
||||
_fillPaint = paint;
|
||||
_fillX1 = x1;
|
||||
_fillY1 = y1;
|
||||
_fillX2 = x2;
|
||||
_fillY2 = y2;
|
||||
_fillDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public class BlazorGraphicsService : IGraphicsService
|
||||
{
|
||||
public List<PathF> ConvertToPaths(PathF aPath, string text, ITextAttributes textual, float ppu, float zoom)
|
||||
{
|
||||
return new List<PathF>();
|
||||
}
|
||||
|
||||
public SizeF GetStringSize(string value, string fontName, float textSize)
|
||||
{
|
||||
return new SizeF(value.Length * 10, textSize + 2);
|
||||
}
|
||||
|
||||
public SizeF GetStringSize(string value, string fontName, float textSize, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
|
||||
{
|
||||
return new SizeF(value.Length * 10, textSize + 2);
|
||||
}
|
||||
|
||||
public void LayoutText(PathF path, string text, ITextAttributes textAttributes, LayoutLine callback)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public IImage LoadImageFromStream(Stream stream, ImageFormat format = ImageFormat.Png)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
using (stream)
|
||||
{
|
||||
stream.CopyTo(memoryStream);
|
||||
}
|
||||
|
||||
return new VirtualImage(memoryStream.ToArray(), format);
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapExportContext CreateBitmapExportContext(int width, int height, float displayScale = 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string SystemFontName => "Arial";
|
||||
public string BoldSystemFontName => "Arial-Bold";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public class CanvasRenderingContext2D : RenderingContext
|
||||
{
|
||||
#region Constants
|
||||
private const string CONTEXT_NAME = "Canvas2d";
|
||||
private const string FILL_STYLE_PROPERTY = "fillStyle";
|
||||
private const string STROKE_STYLE_PROPERTY = "strokeStyle";
|
||||
private const string FILL_RECT_METHOD = "fillRect";
|
||||
private const string CLEAR_RECT_METHOD = "clearRect";
|
||||
private const string STROKE_RECT_METHOD = "strokeRect";
|
||||
private const string FILL_TEXT_METHOD = "fillText";
|
||||
private const string STROKE_TEXT_METHOD = "strokeText";
|
||||
private const string MEASURE_TEXT_METHOD = "measureText";
|
||||
private const string LINE_WIDTH_PROPERTY = "lineWidth";
|
||||
private const string LINE_CAP_PROPERTY = "lineCap";
|
||||
private const string LINE_JOIN_PROPERTY = "lineJoin";
|
||||
private const string MITER_LIMIT_PROPERTY = "miterLimit";
|
||||
private const string GET_LINE_DASH_METHOD = "getLineDash";
|
||||
private const string SET_LINE_DASH_METHOD = "setLineDash";
|
||||
private const string LINE_DASH_OFFSET_PROPERTY = "lineDashOffset";
|
||||
private const string SHADOW_BLUR_PROPERTY = "shadowBlur";
|
||||
private const string SHADOW_COLOR_PROPERTY = "shadowColor";
|
||||
private const string SHADOW_OFFSET_X_PROPERTY = "shadowOffsetX";
|
||||
private const string SHADOW_OFFSET_Y_PROPERTY = "shadowOffsetY";
|
||||
private const string BEGIN_PATH_METHOD = "beginPath";
|
||||
private const string CLOSE_PATH_METHOD = "closePath";
|
||||
private const string MOVE_TO_METHOD = "moveTo";
|
||||
private const string LINE_TO_METHOD = "lineTo";
|
||||
private const string BEZIER_CURVE_TO_METHOD = "bezierCurveTo";
|
||||
private const string QUADRATIC_CURVE_TO_METHOD = "quadraticCurveTo";
|
||||
private const string ARC_METHOD = "arc";
|
||||
private const string ARC_TO_METHOD = "arcTo";
|
||||
private const string RECT_METHOD = "rect";
|
||||
private const string ELLIPSE_METHOD = "ellipse";
|
||||
private const string FILL_METHOD = "fill";
|
||||
private const string STROKE_METHOD = "stroke";
|
||||
private const string DRAW_FOCUS_IF_NEEDED_METHOD = "drawFocusIfNeeded";
|
||||
private const string SCROLL_PATH_INTO_VIEW_METHOD = "scrollPathIntoView";
|
||||
private const string CLIP_METHOD = "clip";
|
||||
private const string IS_POINT_IN_PATH_METHOD = "isPointInPath";
|
||||
private const string IS_POINT_IN_STROKE_METHOD = "isPointInStroke";
|
||||
private const string RESET_TRANSFORM_METHOD = "restTransform";
|
||||
private const string ROTATE_METHOD = "rotate";
|
||||
private const string SCALE_METHOD = "scale";
|
||||
private const string TRANSLATE_METHOD = "translate";
|
||||
private const string TRANSFORM_METHOD = "transform";
|
||||
private const string SET_TRANSFORM_METHOD = "setTransform";
|
||||
private const string GLOBAL_ALPHA_PROPERTY = "globalAlpha";
|
||||
private const string SAVE_METHOD = "save";
|
||||
private const string RESTORE_METHOD = "restore";
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
private string _fillStyle = "#000";
|
||||
|
||||
public string FillStyle
|
||||
{
|
||||
get => this._fillStyle;
|
||||
set
|
||||
{
|
||||
this._fillStyle = value;
|
||||
|
||||
this.SetProperty(FILL_STYLE_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string _strokeStyle = "#000";
|
||||
|
||||
public string StrokeStyle
|
||||
{
|
||||
get => this._strokeStyle;
|
||||
set
|
||||
{
|
||||
this._strokeStyle = value;
|
||||
this.SetProperty(STROKE_STYLE_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string _font = "10px sans-serif";
|
||||
|
||||
public string Font
|
||||
{
|
||||
get => this._font;
|
||||
set
|
||||
{
|
||||
this._font = value;
|
||||
this.SetProperty("font", value);
|
||||
}
|
||||
}
|
||||
|
||||
private TextAlign _textAlign;
|
||||
|
||||
public TextAlign TextAlign
|
||||
{
|
||||
get => this._textAlign;
|
||||
set
|
||||
{
|
||||
this._textAlign = value;
|
||||
this.SetProperty("textAlign", value.ToString().ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
private TextDirection _direction;
|
||||
|
||||
public TextDirection Direction
|
||||
{
|
||||
get => this._direction;
|
||||
set
|
||||
{
|
||||
this._direction = value;
|
||||
this.SetProperty("direction", value.ToString().ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
private TextBaseline _textBaseline;
|
||||
|
||||
public TextBaseline TextBaseline
|
||||
{
|
||||
get => this._textBaseline;
|
||||
set
|
||||
{
|
||||
this._textBaseline = value;
|
||||
this.SetProperty("textBaseline", value.ToString().ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
private float _lineWidth = 1.0f;
|
||||
|
||||
public float LineWidth
|
||||
{
|
||||
get => this._lineWidth;
|
||||
set
|
||||
{
|
||||
this._lineWidth = value;
|
||||
this.SetProperty(LINE_WIDTH_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private LineCap _lineCap;
|
||||
|
||||
public LineCap LineCap
|
||||
{
|
||||
get => this._lineCap;
|
||||
set
|
||||
{
|
||||
this._lineCap = value;
|
||||
this.SetProperty(LINE_CAP_PROPERTY, value.ToString().ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
private LineJoin _lineJoin;
|
||||
|
||||
public LineJoin LineJoin
|
||||
{
|
||||
get => this._lineJoin;
|
||||
set
|
||||
{
|
||||
this._lineJoin = value;
|
||||
this.SetProperty(LINE_JOIN_PROPERTY, value.ToString().ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
private float _miterLimit = 10;
|
||||
|
||||
public float MiterLimit
|
||||
{
|
||||
get => this._miterLimit;
|
||||
set
|
||||
{
|
||||
this._miterLimit = value;
|
||||
this.SetProperty(MITER_LIMIT_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _lineDashOffset;
|
||||
|
||||
public float LineDashOffset
|
||||
{
|
||||
get => this._lineDashOffset;
|
||||
set
|
||||
{
|
||||
this._lineDashOffset = value;
|
||||
this.SetProperty(LINE_DASH_OFFSET_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _shadowBlur;
|
||||
|
||||
public float ShadowBlur
|
||||
{
|
||||
get => this._shadowBlur;
|
||||
set
|
||||
{
|
||||
this._shadowBlur = value;
|
||||
this.SetProperty(SHADOW_BLUR_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string _shadowColor = "black";
|
||||
|
||||
public string ShadowColor
|
||||
{
|
||||
get => this._shadowColor;
|
||||
set
|
||||
{
|
||||
this._shadowColor = value;
|
||||
this.SetProperty(SHADOW_COLOR_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _shadowOffsetX;
|
||||
|
||||
public float ShadowOffsetX
|
||||
{
|
||||
get => this._shadowOffsetX;
|
||||
set
|
||||
{
|
||||
this._shadowOffsetX = value;
|
||||
this.SetProperty(SHADOW_OFFSET_X_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _shadowOffsetY;
|
||||
|
||||
public float ShadowOffsetY
|
||||
{
|
||||
get => this._shadowOffsetY;
|
||||
set
|
||||
{
|
||||
this._shadowOffsetY = value;
|
||||
this.SetProperty(SHADOW_OFFSET_Y_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _globalAlpha = 1.0f;
|
||||
|
||||
public float GlobalAlpha
|
||||
{
|
||||
get => this._globalAlpha;
|
||||
set
|
||||
{
|
||||
this._globalAlpha = value;
|
||||
this.SetProperty(GLOBAL_ALPHA_PROPERTY, value);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal CanvasRenderingContext2D(CanvasComponentBase reference) : base(reference, CONTEXT_NAME)
|
||||
{
|
||||
}
|
||||
|
||||
#region Methods
|
||||
public void FillRect(double x, double y, double width, double height) => this.CallMethod<object>(FILL_RECT_METHOD, x, y, width, height);
|
||||
public void ClearRect(double x, double y, double width, double height) => this.CallMethod<object>(CLEAR_RECT_METHOD, x, y, width, height);
|
||||
public void StrokeRect(double x, double y, double width, double height) => this.CallMethod<object>(STROKE_RECT_METHOD, x, y, width, height);
|
||||
public void FillText(string text, double x, double y, double? maxWidth = null) => this.CallMethod<object>(FILL_TEXT_METHOD, maxWidth.HasValue ? new object[] { text, x, y, maxWidth.Value } : new object[] { text, x, y });
|
||||
public void StrokeText(string text, double x, double y, double? maxWidth = null) => this.CallMethod<object>(STROKE_TEXT_METHOD, maxWidth.HasValue ? new object[] { text, x, y, maxWidth.Value } : new object[] { text, x, y });
|
||||
public TextMetrics MeasureText(string text) => this.CallMethod<TextMetrics>(MEASURE_TEXT_METHOD, text);
|
||||
public float[] GetLineDash() => this.CallMethod<float[]>(GET_LINE_DASH_METHOD);
|
||||
public void SetLineDash(float[] segments) => this.CallMethod<object>(SET_LINE_DASH_METHOD, segments);
|
||||
public void BeginPath() => this.CallMethod<object>(BEGIN_PATH_METHOD);
|
||||
public void ClosePath() => this.CallMethod<object>(CLOSE_PATH_METHOD);
|
||||
public void MoveTo(double x, double y) => this.CallMethod<object>(MOVE_TO_METHOD, x, y);
|
||||
public void LineTo(double x, double y) => this.CallMethod<object>(LINE_TO_METHOD, x, y);
|
||||
public void BezierCurveTo(double cp1X, double cp1Y, double cp2X, double cp2Y, double x, double y) => this.CallMethod<object>(BEZIER_CURVE_TO_METHOD, cp1X, cp1Y, cp2X, cp2Y, x, y);
|
||||
public void QuadraticCurveTo(double cpx, double cpy, double x, double y) => this.CallMethod<object>(QUADRATIC_CURVE_TO_METHOD, cpx, cpy, x, y);
|
||||
public void Arc(double x, double y, double radius, double startAngle, double endAngle, bool? anticlockwise = null) => this.CallMethod<object>(ARC_METHOD, anticlockwise.HasValue ? new object[] { x, y, radius, startAngle, endAngle, anticlockwise.Value } : new object[] { x, y, radius, startAngle, endAngle });
|
||||
public void ArcTo(double x1, double y1, double x2, double y2, double radius) => this.CallMethod<object>(ARC_TO_METHOD, x1, y1, x2, y2, radius);
|
||||
public void Rect(double x, double y, double width, double height) => this.CallMethod<object>(RECT_METHOD, x, y, width, height);
|
||||
public void Ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool anticlockwise = false) => this.CallMethod<object>(ELLIPSE_METHOD, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
|
||||
public void Fill() => this.CallMethod<object>(FILL_METHOD);
|
||||
public void Stroke() => this.CallMethod<object>(STROKE_METHOD);
|
||||
public void DrawFocusIfNeeded(ElementRef elementReference) => this.CallMethod<object>(DRAW_FOCUS_IF_NEEDED_METHOD, elementReference);
|
||||
public void ScrollPathIntoView() => this.CallMethod<object>(SCROLL_PATH_INTO_VIEW_METHOD);
|
||||
public void Clip(string fillRule = "nonzero") => this.CallMethod<object>(CLIP_METHOD, fillRule);
|
||||
public bool IsPointInPath(double x, double y) => this.CallMethod<bool>(IS_POINT_IN_PATH_METHOD, x, y);
|
||||
public bool IsPointInStroke(double x, double y) => this.CallMethod<bool>(IS_POINT_IN_STROKE_METHOD, x, y);
|
||||
public void ResetTransform() => this.CallMethod<object>(RESET_TRANSFORM_METHOD);
|
||||
public void Rotate(float angle) => this.CallMethod<object>(ROTATE_METHOD, angle);
|
||||
public void Scale(double x, double y) => this.CallMethod<object>(SCALE_METHOD, x, y);
|
||||
public void Translate(double x, double y) => this.CallMethod<object>(TRANSLATE_METHOD, x, y);
|
||||
public void Transform(double m11, double m12, double m21, double m22, double dx, double dy) => this.CallMethod<object>(TRANSFORM_METHOD, m11, m12, m21, m22, dx, dy);
|
||||
public void SetTransform(double m11, double m12, double m21, double m22, double dx, double dy) => this.CallMethod<object>(SET_TRANSFORM_METHOD, m11, m12, m21, m22, dx, dy);
|
||||
public void Save() => this.CallMethod<object>(SAVE_METHOD);
|
||||
public void Restore() => this.CallMethod<object>(RESTORE_METHOD);
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public enum LineCap
|
||||
{
|
||||
Butt,
|
||||
Round,
|
||||
Square
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public enum LineJoin
|
||||
{
|
||||
Miter,
|
||||
Round,
|
||||
Bevel
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public enum TextAlign
|
||||
{
|
||||
Start,
|
||||
End,
|
||||
Left,
|
||||
Right,
|
||||
Center
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public enum TextBaseline
|
||||
{
|
||||
Alphabetic,
|
||||
Top,
|
||||
Hanging,
|
||||
Middle,
|
||||
Ideographic,
|
||||
Bottom
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public enum TextDirection
|
||||
{
|
||||
Inherit,
|
||||
LTR,
|
||||
RTL
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace System.Graphics.Blazor.Canvas2D
|
||||
{
|
||||
public class TextMetrics
|
||||
{
|
||||
public double Width { get; set; }
|
||||
public double ActualBoundingBoxLeft { get; set; }
|
||||
public double ActualBoundingBoxRight { get; set; }
|
||||
public double FontBoundingBoxAscent { get; set; }
|
||||
public double FontBoundingBoxDescent { get; set; }
|
||||
public double ActualBoundingBoxAscent { get; set; }
|
||||
public double ActualBoundingBoxDescent { get; set; }
|
||||
public double EmHeightAscent { get; set; }
|
||||
public double EmHeightDescent { get; set; }
|
||||
public double HangingBaseline { get; set; }
|
||||
public double AlphabeticBaseline { get; set; }
|
||||
public double IdeographicBaseline { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
@inherits global::System.Graphics.Blazor.CanvasComponentBase
|
||||
|
||||
<canvas id="@id" width="@width" height="@height" ref="@canvas"></canvas>
|
|
@ -0,0 +1,56 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public class CanvasComponentBase : ComponentBase
|
||||
{
|
||||
private BlazorCanvas _blazorCanvas;
|
||||
|
||||
[Parameter]
|
||||
protected long width { get; set; }
|
||||
|
||||
[Parameter]
|
||||
protected long height { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
protected readonly string id = Guid.NewGuid().ToString();
|
||||
|
||||
protected ElementRef canvas;
|
||||
|
||||
internal ElementRef CanvasReference => canvas;
|
||||
|
||||
public string Id => id;
|
||||
|
||||
private bool _initialized;
|
||||
private float _displayScale = 1;
|
||||
|
||||
public BlazorCanvas BlazorCanvas
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_blazorCanvas == null)
|
||||
{
|
||||
_blazorCanvas = new BlazorCanvas
|
||||
{
|
||||
Context = new Canvas2D.CanvasRenderingContext2D(this),
|
||||
DisplayScale = _displayScale
|
||||
};
|
||||
}
|
||||
|
||||
return _blazorCanvas;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnAfterRender()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
_initialized = true;
|
||||
_displayScale = await JSRuntime.SetupCanvas(Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<div class="my-component">
|
||||
This Blazor component is defined in the <strong>System.Graphics.Blazor</strong> package.
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
using System.Graphics.Blazor.Canvas2D;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public static class ComponentExtensions
|
||||
{
|
||||
public static CanvasRenderingContext2D CreateRenderingContext2D(this CanvasComponentBase canvas)
|
||||
{
|
||||
return new CanvasRenderingContext2D(canvas);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public static class JavascriptInterop
|
||||
{
|
||||
public static Task<float> SetupCanvas(
|
||||
this IJSRuntime runtime,
|
||||
string id)
|
||||
{
|
||||
return runtime.InvokeAsync<float>(
|
||||
"EWGraphicsInterop.SetupCanvas",
|
||||
id );
|
||||
}
|
||||
|
||||
public static Task<bool> PointIsInPath(
|
||||
this IJSRuntime runtime,
|
||||
PathF path,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
var pathDefinition = path.ToDefinitionString();
|
||||
return PointIsInPath(runtime, pathDefinition, x, y);
|
||||
}
|
||||
|
||||
public static Task<bool> PointIsInPath(
|
||||
this IJSRuntime runtime,
|
||||
string path,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
return runtime.InvokeAsync<bool>(
|
||||
"EWGraphicsInterop.PointIsInPath",
|
||||
path,
|
||||
x,
|
||||
y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace System.Graphics.Blazor
|
||||
{
|
||||
public abstract class RenderingContext : IDisposable
|
||||
{
|
||||
private const string NamespacePrefix = "BlazorExtensions";
|
||||
private const string SetPropertyAction = "setProperty";
|
||||
private const string GetPropertyAction = "getProperty";
|
||||
private const string CallMethodAction = "call";
|
||||
private const string AddAction = "add";
|
||||
private const string RemoveAction = "remove";
|
||||
|
||||
private readonly string _contextName;
|
||||
private readonly IJSInProcessRuntime _runtime;
|
||||
|
||||
public ElementRef Canvas { get; }
|
||||
|
||||
|
||||
internal RenderingContext(CanvasComponentBase reference, string contextName, object parameters = null)
|
||||
{
|
||||
Canvas = reference.CanvasReference;
|
||||
_contextName = contextName;
|
||||
_runtime = (IJSInProcessRuntime)reference.JSRuntime;
|
||||
_runtime.Invoke<object>($"{NamespacePrefix}.{_contextName}.{AddAction}", Canvas, parameters);
|
||||
}
|
||||
|
||||
protected void SetProperty(string property, object value)
|
||||
{
|
||||
_runtime.Invoke<object>($"{NamespacePrefix}.{_contextName}.{SetPropertyAction}", Canvas, property, value);
|
||||
}
|
||||
|
||||
protected T GetProperty<T>(string property)
|
||||
{
|
||||
return _runtime.Invoke<T>($"{NamespacePrefix}.{_contextName}.{GetPropertyAction}", Canvas, property);
|
||||
}
|
||||
|
||||
protected T CallMethod<T>(string method)
|
||||
{
|
||||
return _runtime.Invoke<T>($"{NamespacePrefix}.{_contextName}.{CallMethodAction}", Canvas, method);
|
||||
}
|
||||
|
||||
protected T CallMethod<T>(string method, params object[] value)
|
||||
{
|
||||
return _runtime.Invoke<T>($"{NamespacePrefix}.{_contextName}.{CallMethodAction}", Canvas, method, value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_runtime.Invoke<object>($"{NamespacePrefix}.{_contextName}.{RemoveAction}", Canvas);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable>true</IsPackable>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- .js/.css files will be referenced via <script>/<link> tags; other content files will just be included in the app's 'dist' directory without any tags referencing them -->
|
||||
<EmbeddedResource Include="content\**\*.js" LogicalName="blazor:js:%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
<EmbeddedResource Include="content\**\*.css" LogicalName="blazor:css:%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
<EmbeddedResource Include="content\**" Exclude="**\*.js;**\*.css" LogicalName="blazor:file:%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="content\Interop.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor" Version="0.9.0-preview3-19154-02" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.9.0-preview3-19154-02" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\System.Graphics.Core\System.Graphics.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,338 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Graphics.Blazor.WebGL
|
||||
{
|
||||
public class WebGLContext : RenderingContext
|
||||
{
|
||||
#region Constants
|
||||
private const string CONTEXT_NAME = "WebGL";
|
||||
private const string CLEAR_COLOR = "clearColor";
|
||||
private const string CLEAR = "clear";
|
||||
private const string DRAWING_BUFFER_WIDTH = "drawingBufferWidth";
|
||||
private const string DRAWING_BUFFER_HEIGHT = "drawingBufferHeight";
|
||||
private const string GET_CONTEXT_ATTRIBUTES = "getContextAttributes";
|
||||
private const string IS_CONTEXT_LOST = "isContextLost";
|
||||
private const string SCISSOR = "scissor";
|
||||
private const string VIEWPORT = "viewport";
|
||||
private const string ACTIVE_TEXTURE = "activeTexture";
|
||||
private const string BLEND_COLOR = "blendColor";
|
||||
private const string BLEND_EQUATION = "blendEquation";
|
||||
private const string BLEND_EQUATION_SEPARATE = "blendEquationSeparate";
|
||||
private const string BLEND_FUNC = "blendFunc";
|
||||
private const string BLEND_FUNC_SEPARATE = "blendFuncSeparate";
|
||||
private const string CLEAR_DEPTH = "clearDepth";
|
||||
private const string CLEAR_STENCIL = "clearStencil";
|
||||
private const string COLOR_MASK = "colorMask";
|
||||
private const string CULL_FACE = "cullFace";
|
||||
private const string DEPTH_FUNC = "depthFunc";
|
||||
private const string DEPTH_MASK = "depthMask";
|
||||
private const string DEPTH_RANGE = "depthRange";
|
||||
private const string DISABLE = "disable";
|
||||
private const string ENABLE = "enable";
|
||||
private const string FRONT_FACE = "frontFace";
|
||||
private const string GET_PARAMETER = "getParameter";
|
||||
private const string GET_ERROR = "getError";
|
||||
private const string HINT = "hint";
|
||||
private const string IS_ENABLED = "isEnabled";
|
||||
private const string LINE_WIDTH = "lineWidth";
|
||||
private const string PIXEL_STORE_I = "pixelStorei";
|
||||
private const string POLYGON_OFFSET = "polygonOffset";
|
||||
private const string SAMPLE_COVERAGE = "sampleCoverage";
|
||||
private const string STENCIL_FUNC = "stencilFunc";
|
||||
private const string STENCIL_FUNC_SEPARATE = "stencilFuncSeparate";
|
||||
private const string STENCIL_MASK = "stencilMask";
|
||||
private const string STENCIL_MASK_SEPARATE = "stencilMaskSeparate";
|
||||
private const string STENCIL_OP = "stencilOp";
|
||||
private const string STENCIL_OP_SEPARATE = "stencilOpSeparate";
|
||||
private const string BIND_BUFFER = "bindBuffer";
|
||||
private const string BUFFER_DATA = "bufferData";
|
||||
private const string BUFFER_SUB_DATA = "bufferSubData";
|
||||
private const string CREATE_BUFFER = "createBuffer";
|
||||
private const string DELETE_BUFFER = "deleteBuffer";
|
||||
private const string GET_BUFFER_PARAMETER = "getBufferParameter";
|
||||
private const string IS_BUFFER = "isBuffer";
|
||||
private const string BIND_FRAMEBUFFER = "bindFramebuffer";
|
||||
private const string CHECK_FRAMEBUFFER_STATUS = "checkFramebufferStatus";
|
||||
private const string CREATE_FRAMEBUFFER = "createFramebuffer";
|
||||
private const string DELETE_FRAMEBUFFER = "deleteFramebuffer";
|
||||
private const string FRAMEBUFFER_RENDERBUFFER = "framebufferRenderbuffer";
|
||||
private const string FRAMEBUFFER_TEXTURE_2D = "framebufferTexture2D";
|
||||
private const string GET_FRAMEBUFFER_ATTACHMENT_PARAMETER = "getFramebufferAttachmentParameter";
|
||||
private const string IS_FRAMEBUFFER = "isFramebuffer";
|
||||
private const string READ_PIXELS = "readPixels";
|
||||
private const string BIND_RENDERBUFFER = "bindRenderbuffer";
|
||||
private const string CREATE_RENDERBUFFER = "createRenderbuffer";
|
||||
private const string DELETE_RENDERBUFFER = "deleteRenderbuffer";
|
||||
private const string GET_RENDERBUFFER_PARAMETER = "getRenderbufferParameter";
|
||||
private const string IS_RENDERBUFFER = "isRenderbuffer";
|
||||
private const string RENDERBUFFER_STORAGE = "renderbufferStorage";
|
||||
private const string BIND_TEXTURE = "bindTexture";
|
||||
private const string COPY_TEX_IMAGE_2D = "copyTexImage2D";
|
||||
private const string COPY_TEX_SUB_IMAGE_2D = "copyTexSubImage2D";
|
||||
private const string CREATE_TEXTURE = "createTexture";
|
||||
private const string DELETE_TEXTURE = "deleteTexture";
|
||||
private const string GENERATE_MIPMAP = "generateMipmap";
|
||||
private const string GET_TEX_PARAMETER = "getTexParameter";
|
||||
private const string IS_TEXTURE = "isTexture";
|
||||
private const string TEX_IMAGE_2D = "texImage2D";
|
||||
private const string TEX_SUB_IMAGE_2D = "texSubImage2D";
|
||||
private const string TEX_PARAMETER_F = "texParameterf";
|
||||
private const string TEX_PARAMETER_I = "texParameteri";
|
||||
private const string ATTACH_SHADER = "attachShader";
|
||||
private const string BIND_ATTRIB_LOCATION = "bindAttribLocation";
|
||||
private const string COMPILE_SHADER = "compileShader";
|
||||
private const string CREATE_PROGRAM = "createProgram";
|
||||
private const string CREATE_SHADER = "createShader";
|
||||
private const string DELETE_PROGRAM = "deleteProgram";
|
||||
private const string DELETE_SHADER = "deleteShader";
|
||||
private const string DETACH_SHADER = "detachShader";
|
||||
private const string GET_ATTACHED_SHADERS = "getAttachedShaders";
|
||||
private const string GET_PROGRAM_PARAMETER = "getProgramParameter";
|
||||
private const string GET_PROGRAM_INFO_LOG = "getProgramInfoLog";
|
||||
private const string GET_SHADER_PARAMETER = "getShaderParameter";
|
||||
private const string GET_SHADER_PRECISION_FORMAT = "getShaderPrecisionFormat";
|
||||
private const string GET_SHADER_INFO_LOG = "getShaderInfoLog";
|
||||
private const string GET_SHADER_SOURCE = "getShaderSource";
|
||||
private const string IS_PROGRAM = "isProgram";
|
||||
private const string IS_SHADER = "isShader";
|
||||
private const string LINK_PROGRAM = "linkProgram";
|
||||
private const string SHADER_SOURCE = "shaderSource";
|
||||
private const string USE_PROGRAM = "useProgram";
|
||||
private const string VALIDATE_PROGRAM = "validateProgram";
|
||||
private const string DISABLE_VERTEX_ATTRIB_ARRAY = "disableVertexAttribArray";
|
||||
private const string ENABLE_VERTEX_ATTRIB_ARRAY = "enableVertexAttribArray";
|
||||
private const string GET_ACTIVE_ATTRIB = "getActiveAttrib";
|
||||
private const string GET_ACTIVE_UNIFORM = "getActiveUniform";
|
||||
private const string GET_ATTRIB_LOCATION = "getAttribLocation";
|
||||
private const string GET_UNIFORM = "getUniform";
|
||||
private const string GET_UNIFORM_LOCATION = "getUniformLocation";
|
||||
private const string GET_VERTEX_ATTRIB = "getVertexAttrib";
|
||||
private const string GET_VERTEX_ATTRIB_OFFSET = "getVertexAttribOffset";
|
||||
private const string UNIFORM = "uniform";
|
||||
private const string UNIFORM_MATRIX = "uniformMatrix";
|
||||
private const string VERTEX_ATTRIB = "vertexAttrib";
|
||||
private const string VERTEX_ATTRIB_POINTER = "vertexAttribPointer";
|
||||
private const string DRAW_ARRAYS = "drawArrays";
|
||||
private const string DRAW_ELEMENTS = "drawElements";
|
||||
private const string FINISH = "finish";
|
||||
private const string FLUSH = "flush";
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public int DrawingBufferWidth => this.GetProperty<int>(DRAWING_BUFFER_WIDTH);
|
||||
public int DrawingBufferHeight => this.GetProperty<int>(DRAWING_BUFFER_HEIGHT);
|
||||
#endregion
|
||||
|
||||
internal WebGLContext(CanvasComponentBase reference, WebGLContextAttributes attributes = null) : base(reference, CONTEXT_NAME, attributes)
|
||||
{
|
||||
}
|
||||
|
||||
#region Methods
|
||||
public void ClearColor(float red, float green, float blue, float alpha) => this.CallMethod<object>(CLEAR_COLOR, red, green, blue, alpha);
|
||||
public void Clear(BufferBits mask) => this.CallMethod<object>(CLEAR, mask);
|
||||
public WebGLContextAttributes GetContextAttributes() => this.CallMethod<WebGLContextAttributes>(GET_CONTEXT_ATTRIBUTES);
|
||||
public bool IsContextLost() => this.CallMethod<bool>(IS_CONTEXT_LOST);
|
||||
public void Scissor(int x, int y, int width, int height) => this.CallMethod<object>(SCISSOR, x, y, width, height);
|
||||
public void Viewport(int x, int y, int width, int height) => this.CallMethod<object>(VIEWPORT, x, y, width, height);
|
||||
public void ActiveTexture(Texture texture) => this.CallMethod<object>(ACTIVE_TEXTURE, texture);
|
||||
public void BlendColor(float red, float green, float blue, float alpha) => this.CallMethod<object>(BLEND_COLOR, red, green, blue, alpha);
|
||||
public void BlendEquation(BlendingEquation equation) => this.CallMethod<object>(BLEND_EQUATION, equation);
|
||||
public void BlendEquationSeparate(BlendingEquation modeRGB, BlendingEquation modeAlpha) => this.CallMethod<object>(BLEND_EQUATION_SEPARATE, modeRGB, modeAlpha);
|
||||
public void BlendFunc(BlendingMode sfactor, BlendingMode dfactor) => this.CallMethod<object>(BLEND_FUNC, sfactor, dfactor);
|
||||
public void BlendFuncSeparate(BlendingMode srcRGB, BlendingMode dstRGB, BlendingMode srcAlpha, BlendingMode dstAlpha) => this.CallMethod<object>(BLEND_FUNC_SEPARATE, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
public void ClearDepth(float depth) => this.CallMethod<object>(CLEAR_DEPTH, depth);
|
||||
public void ClearStencil(int stencil) => this.CallMethod<object>(CLEAR_STENCIL, stencil);
|
||||
public void ColorMask(bool red, bool green, bool blue, bool alpha) => this.CallMethod<object>(COLOR_MASK, red, green, blue, alpha);
|
||||
public void CullFace(Face mode) => this.CallMethod<object>(CULL_FACE, mode);
|
||||
public void DepthFunc(CompareFunction func) => this.CallMethod<object>(DEPTH_FUNC, func);
|
||||
public void DepthMask(bool flag) => this.CallMethod<object>(DEPTH_MASK, flag);
|
||||
public void DepthRange(float zNear, float zFar) => this.CallMethod<object>(DEPTH_RANGE, zNear, zFar);
|
||||
public void Disable(EnableCap cap) => this.CallMethod<object>(DISABLE, cap);
|
||||
public void Enable(EnableCap cap) => this.CallMethod<object>(ENABLE, cap);
|
||||
public void FrontFace(FrontFaceDirection mode) => this.CallMethod<object>(FRONT_FACE, mode);
|
||||
public T GetParameter<T>(Parameter parameter) => this.CallMethod<T>(GET_PARAMETER, parameter);
|
||||
public Error GetError() => this.CallMethod<Error>(GET_ERROR);
|
||||
public void Hint(HintTarget target, HintMode mode) => this.CallMethod<object>(HINT, target, mode);
|
||||
public bool IsEnabled(EnableCap cap) => this.CallMethod<bool>(IS_ENABLED, cap);
|
||||
public bool LineWidth(float width) => this.CallMethod<bool>(LINE_WIDTH, width);
|
||||
public bool PixelStoreI(PixelStorageMode pname, int param) => this.CallMethod<bool>(PIXEL_STORE_I, pname, param);
|
||||
public void PolygonOffset(float factor, float units) => this.CallMethod<object>(POLYGON_OFFSET, factor, units);
|
||||
public void SampleCoverage(float value, bool invert) => this.CallMethod<object>(SAMPLE_COVERAGE, value, invert);
|
||||
public void StencilFunc(CompareFunction func, int reference, uint mask) => this.CallMethod<object>(STENCIL_FUNC, func, reference, mask);
|
||||
public void StencilFuncSeparate(Face face, CompareFunction func, int reference, uint mask) => this.CallMethod<object>(STENCIL_FUNC_SEPARATE, face, func, reference, mask);
|
||||
public void StencilMask(uint mask) => this.CallMethod<object>(STENCIL_MASK, mask);
|
||||
public void StencilMaskSeparate(Face face, uint mask) => this.CallMethod<object>(STENCIL_MASK_SEPARATE, face, mask);
|
||||
public void StencilOp(StencilFunction fail, StencilFunction zfail, StencilFunction zpass) => this.CallMethod<object>(STENCIL_OP, fail, zfail, zpass);
|
||||
public void StencilOpSeparate(Face face, StencilFunction fail, StencilFunction zfail, StencilFunction zpass) => this.CallMethod<object>(STENCIL_OP_SEPARATE, face, fail, zfail, zpass);
|
||||
|
||||
public void BindBuffer(BufferType target, WebGLBuffer buffer) => this.CallMethod<object>(BIND_BUFFER, target, buffer);
|
||||
public void BufferData(BufferType target, int size, BufferUsageHint usage) => this.CallMethod<object>(BUFFER_DATA, target, size, usage);
|
||||
public void BufferData<T>(BufferType target, T[] data, BufferUsageHint usage) => this.CallMethod<object>(BUFFER_DATA, target, this.ConvertToByteArray(data), usage);
|
||||
public void BufferSubData<T>(BufferType target, uint offset, T[] data) => this.CallMethod<object>(BUFFER_SUB_DATA, target, offset, this.ConvertToByteArray(data));
|
||||
public WebGLBuffer CreateBuffer() => this.CallMethod<WebGLBuffer>(CREATE_BUFFER);
|
||||
public void DeleteBuffer(WebGLBuffer buffer) => this.CallMethod<WebGLBuffer>(DELETE_BUFFER, buffer);
|
||||
public T GetBufferParameter<T>(BufferType target, BufferParameter pname) => this.CallMethod<T>(GET_BUFFER_PARAMETER, target, pname);
|
||||
public bool IsBuffer(WebGLBuffer buffer) => this.CallMethod<bool>(IS_BUFFER, buffer);
|
||||
|
||||
public void BindFramebuffer(FramebufferType target, WebGLFramebuffer framebuffer) => this.CallMethod<object>(BIND_FRAMEBUFFER, target, framebuffer);
|
||||
public FramebufferStatus CheckFramebufferStatus(FramebufferType target) => this.CallMethod<FramebufferStatus>(CHECK_FRAMEBUFFER_STATUS, target);
|
||||
public WebGLFramebuffer CreateFramebuffer() => this.CallMethod<WebGLFramebuffer>(CREATE_FRAMEBUFFER);
|
||||
public void DeleteFramebuffer(WebGLFramebuffer buffer) => this.CallMethod<object>(DELETE_FRAMEBUFFER, buffer);
|
||||
public void FramebufferRenderbuffer(FramebufferType target, FramebufferAttachment attachment, RenderbufferType renderbuffertarget, WebGLRenderbuffer renderbuffer) => this.CallMethod<object>(FRAMEBUFFER_RENDERBUFFER, target, attachment, renderbuffertarget, renderbuffer);
|
||||
public void FramebufferTexture2D(FramebufferType target, FramebufferAttachment attachment, Texture2DType textarget, WebGLTexture texture, int level) => this.CallMethod<object>(FRAMEBUFFER_TEXTURE_2D, target, attachment, textarget, texture, level);
|
||||
public T GetFramebufferAttachmentParameter<T>(FramebufferType target, FramebufferAttachment attachment, FramebufferAttachmentParameter pname) => this.CallMethod<T>(GET_FRAMEBUFFER_ATTACHMENT_PARAMETER, target, attachment, pname);
|
||||
public bool IsFramebuffer(WebGLFramebuffer framebuffer) => this.CallMethod<bool>(IS_FRAMEBUFFER, framebuffer);
|
||||
public void ReadPixels(int x, int y, int width, int height, PixelFormat format, PixelType type, byte[] pixels) => this.CallMethod<object>(READ_PIXELS, x, y, width, height, format, type, pixels); //pixels should be an ArrayBufferView which the data gets read into
|
||||
|
||||
public void BindRenderbuffer(RenderbufferType target, WebGLRenderbuffer renderbuffer) => this.CallMethod<object>(BIND_RENDERBUFFER, target, renderbuffer);
|
||||
public WebGLRenderbuffer CreateRenderbuffer() => this.CallMethod<WebGLRenderbuffer>(CREATE_RENDERBUFFER);
|
||||
public void DeleteRenderbuffer(WebGLRenderbuffer buffer) => this.CallMethod<object>(DELETE_RENDERBUFFER, buffer);
|
||||
public T GetRenderbufferParameter<T>(RenderbufferType target, RenderbufferParameter pname) => this.CallMethod<T>(GET_RENDERBUFFER_PARAMETER, target, pname);
|
||||
public bool IsRenderbuffer(WebGLRenderbuffer renderbuffer) => this.CallMethod<bool>(IS_RENDERBUFFER, renderbuffer);
|
||||
public void RenderbufferStorage(RenderbufferType type, RenderbufferFormat internalFormat, int width, int height) => this.CallMethod<object>(RENDERBUFFER_STORAGE, type, internalFormat, width, height);
|
||||
|
||||
public void BindTexture(TextureType type, WebGLTexture texture) => this.CallMethod<object>(BIND_TEXTURE, type, texture);
|
||||
public void CopyTexImage2D(Texture2DType target, int level, PixelFormat format, int x, int y, int width, int height, int border) => this.CallMethod<object>(COPY_TEX_IMAGE_2D, target, level, format, x, y, width, height, border);
|
||||
public void CopyTexSubImage2D(Texture2DType target, int level, int xoffset, int yoffset, int x, int y, int width, int height) => this.CallMethod<object>(COPY_TEX_SUB_IMAGE_2D, target, level, xoffset, yoffset, x, y, width, height);
|
||||
public WebGLTexture CreateTexture() => this.CallMethod<WebGLTexture>(CREATE_TEXTURE);
|
||||
public void DeleteTexture(WebGLTexture texture) => this.CallMethod<object>(DELETE_TEXTURE, texture);
|
||||
public void GenerateMipmap(TextureType target) => this.CallMethod<object>(GENERATE_MIPMAP, target);
|
||||
public T GetTexParameter<T>(TextureType target, TextureParameter pname) => this.CallMethod<T>(GET_TEX_PARAMETER, target, pname);
|
||||
public bool IsTexture(WebGLTexture texture) => this.CallMethod<bool>(IS_TEXTURE, texture);
|
||||
public void TexImage2D<T>(Texture2DType target, int level, PixelFormat internalFormat, int width, int height, PixelFormat format, PixelType type, T[] pixels)
|
||||
where T : struct
|
||||
=> this.CallMethod<object>(TEX_IMAGE_2D, target, level, internalFormat, width, height, format, type, pixels);
|
||||
public void TexSubImage2D<T>(Texture2DType target, int level, int xoffset, int yoffset, int width, int height, PixelFormat format, PixelType type, T[] pixels)
|
||||
where T : struct
|
||||
=> this.CallMethod<object>(TEX_SUB_IMAGE_2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
public void TexParameter(TextureType target, TextureParameter pname, float param) => this.CallMethod<object>(TEX_PARAMETER_F, target, pname, param);
|
||||
public void TexParameter(TextureType target, TextureParameter pname, int param) => this.CallMethod<object>(TEX_PARAMETER_I, target, pname, param);
|
||||
|
||||
public void AttachShader(WebGLProgram program, WebGLShader shader) => this.CallMethod<object>(ATTACH_SHADER, program, shader);
|
||||
public void BindAttribLocation(WebGLProgram program, uint index, string name) => this.CallMethod<object>(BIND_ATTRIB_LOCATION, program, index, name);
|
||||
public void CompileShader(WebGLShader shader) => this.CallMethod<object>(COMPILE_SHADER, shader);
|
||||
public WebGLProgram CreateProgram() => this.CallMethod<WebGLProgram>(CREATE_PROGRAM);
|
||||
public WebGLShader CreateShader(ShaderType type) => this.CallMethod<WebGLShader>(CREATE_SHADER, type);
|
||||
public void DeleteProgram(WebGLProgram program) => this.CallMethod<object>(DELETE_PROGRAM, program);
|
||||
public void DeleteShader(WebGLShader shader) => this.CallMethod<object>(DELETE_SHADER, shader);
|
||||
public void DetachShader(WebGLProgram program, WebGLShader shader) => this.CallMethod<object>(DETACH_SHADER, program, shader);
|
||||
public WebGLShader[] GetAttachedShaders(WebGLProgram program) => this.CallMethod<WebGLShader[]>(GET_ATTACHED_SHADERS, program);
|
||||
public T GetProgramParameter<T>(WebGLProgram program, ProgramParameter pname) => this.CallMethod<T>(GET_PROGRAM_PARAMETER, program, pname);
|
||||
public string GetProgramInfoLog(WebGLProgram program) => this.CallMethod<string>(GET_PROGRAM_INFO_LOG, program);
|
||||
public T GetShaderParameter<T>(WebGLShader shader, ShaderParameter pname) => this.CallMethod<T>(GET_SHADER_PARAMETER, shader, pname);
|
||||
public WebGLShaderPrecisionFormat GetShaderPrecisionFormat(ShaderType shaderType, ShaderPrecision precisionType) => this.CallMethod<WebGLShaderPrecisionFormat>(GET_SHADER_PRECISION_FORMAT, shaderType, precisionType);
|
||||
public string GetShaderInfoLog(WebGLShader shader) => this.CallMethod<string>(GET_SHADER_INFO_LOG, shader);
|
||||
public string GetShaderSource(WebGLShader shader) => this.CallMethod<string>(GET_SHADER_SOURCE, shader);
|
||||
public bool IsProgram(WebGLProgram program) => this.CallMethod<bool>(IS_PROGRAM, program);
|
||||
public bool IsShader(WebGLShader shader) => this.CallMethod<bool>(IS_SHADER, shader);
|
||||
public void LinkProgram(WebGLProgram program) => this.CallMethod<object>(LINK_PROGRAM, program);
|
||||
public void ShaderSource(WebGLShader shader, string source) => this.CallMethod<object>(SHADER_SOURCE, shader, source);
|
||||
public void UseProgram(WebGLProgram program) => this.CallMethod<object>(USE_PROGRAM, program);
|
||||
public void ValidateProgram(WebGLProgram program) => this.CallMethod<object>(VALIDATE_PROGRAM, program);
|
||||
|
||||
public void DisableVertexAttribArray(uint index) => this.CallMethod<object>(DISABLE_VERTEX_ATTRIB_ARRAY, index);
|
||||
public void EnableVertexAttribArray(uint index) => this.CallMethod<object>(ENABLE_VERTEX_ATTRIB_ARRAY, index);
|
||||
public WebGLActiveInfo GetActiveAttrib(WebGLProgram program, uint index) => this.CallMethod<WebGLActiveInfo>(GET_ACTIVE_ATTRIB, program, index);
|
||||
public WebGLActiveInfo GetActiveUniform(WebGLProgram program, uint index) => this.CallMethod<WebGLActiveInfo>(GET_ACTIVE_UNIFORM, program, index);
|
||||
public int GetAttribLocation(WebGLProgram program, string name) => this.CallMethod<int>(GET_ATTRIB_LOCATION, program, name);
|
||||
public T GetUniform<T>(WebGLProgram program, WebGLUniformLocation location) => this.CallMethod<T>(GET_UNIFORM, program, location);
|
||||
public WebGLUniformLocation GetUniformLocation(WebGLProgram program, string name) => this.CallMethod<WebGLUniformLocation>(GET_UNIFORM_LOCATION, program, name);
|
||||
public T GetVertexAttrib<T>(uint index, VertexAttribute pname) => this.CallMethod<T>(GET_VERTEX_ATTRIB, index, pname);
|
||||
public long GetVertexAttribOffset(uint index, VertexAttributePointer pname) => this.CallMethod<long>(GET_VERTEX_ATTRIB_OFFSET, index, pname);
|
||||
public void VertexAttribPointer(uint index, int size, DataType type, bool normalized, int stride, long offset) => this.CallMethod<object>(VERTEX_ATTRIB_POINTER, index, size, type, normalized, stride, offset);
|
||||
|
||||
public void Uniform(WebGLUniformLocation location, params float[] value)
|
||||
{
|
||||
switch (value.Length)
|
||||
{
|
||||
case 1:
|
||||
this.CallMethod<object>(UNIFORM + "1fv", location, value);
|
||||
break;
|
||||
case 2:
|
||||
this.CallMethod<object>(UNIFORM + "2fv", location, value);
|
||||
break;
|
||||
case 3:
|
||||
this.CallMethod<object>(UNIFORM + "3fv", location, value);
|
||||
break;
|
||||
case 4:
|
||||
this.CallMethod<object>(UNIFORM + "4fv", location, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value.Length, "Value array is empty or too long");
|
||||
}
|
||||
}
|
||||
|
||||
public void Uniform(WebGLUniformLocation location, params int[] value)
|
||||
{
|
||||
switch (value.Length)
|
||||
{
|
||||
case 1:
|
||||
this.CallMethod<object>(UNIFORM + "1iv", location, value);
|
||||
break;
|
||||
case 2:
|
||||
this.CallMethod<object>(UNIFORM + "2iv", location, value);
|
||||
break;
|
||||
case 3:
|
||||
this.CallMethod<object>(UNIFORM + "3iv", location, value);
|
||||
break;
|
||||
case 4:
|
||||
this.CallMethod<object>(UNIFORM + "4iv", location, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value.Length, "Value array is empty or too long");
|
||||
}
|
||||
}
|
||||
|
||||
public void UniformMatrix(WebGLUniformLocation location, bool transpose, float[] value)
|
||||
{
|
||||
switch (value.Length)
|
||||
{
|
||||
case 2 * 2:
|
||||
this.CallMethod<object>(UNIFORM_MATRIX + "2fv", location, transpose, value);
|
||||
break;
|
||||
case 3 * 3:
|
||||
this.CallMethod<object>(UNIFORM_MATRIX + "3fv", location, transpose, value);
|
||||
break;
|
||||
case 4 * 4:
|
||||
this.CallMethod<object>(UNIFORM_MATRIX + "4fv", location, transpose, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value.Length, "Value array has incorrect size");
|
||||
}
|
||||
}
|
||||
|
||||
public void VertexAttrib(uint index, params float[] value)
|
||||
{
|
||||
switch (value.Length)
|
||||
{
|
||||
case 1:
|
||||
this.CallMethod<object>(VERTEX_ATTRIB + "1fv", index, value);
|
||||
break;
|
||||
case 2:
|
||||
this.CallMethod<object>(VERTEX_ATTRIB + "2fv", index, value);
|
||||
break;
|
||||
case 3:
|
||||
this.CallMethod<object>(VERTEX_ATTRIB + "3fv", index, value);
|
||||
break;
|
||||
case 4:
|
||||
this.CallMethod<object>(VERTEX_ATTRIB + "4fv", index, value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value.Length, "Value array is empty or too long");
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawArrays(Primitive mode, int first, int count) => this.CallMethod<object>(DRAW_ARRAYS, mode, first, count);
|
||||
public void DrawElements(Primitive mode, int count, DataType type, long offset) => this.CallMethod<object>(DRAW_ELEMENTS, mode, count, type, offset);
|
||||
public void Finish() => this.CallMethod<object>(FINISH);
|
||||
public void Flush() => this.CallMethod<object>(FLUSH);
|
||||
|
||||
private byte[] ConvertToByteArray<T>(T[] arr)
|
||||
{
|
||||
byte[] byteArr = new byte[arr.Length * Marshal.SizeOf<T>()];
|
||||
Buffer.BlockCopy(arr, 0, byteArr, 0, byteArr.Length);
|
||||
return byteArr;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,453 @@
|
|||
namespace System.Graphics.Blazor.WebGL
|
||||
{
|
||||
public enum BufferBits
|
||||
{
|
||||
COLOR_BUFFER_BIT = 0x4000,
|
||||
DEPTH_BUFFER_BIT = 0x100,
|
||||
STENCIL_BUFFER_BIT = 0x400
|
||||
}
|
||||
|
||||
public enum Primitive
|
||||
{
|
||||
POINTS = 0,
|
||||
LINES = 1,
|
||||
LINE_LOOP = 2,
|
||||
LINE_STRIP = 3,
|
||||
TRIANGLES = 4,
|
||||
TRINAGLE_STRIP = 5,
|
||||
TRIANGLE_FAN = 6
|
||||
}
|
||||
|
||||
public enum BlendingMode
|
||||
{
|
||||
ZERO = 0,
|
||||
ONE = 1,
|
||||
SRC_COLOR = 0x300,
|
||||
ONE_MINUS_SRC_COLOR = 0x301,
|
||||
SRC_ALPHA = 0x302,
|
||||
ONE_MINUS_SRC_ALPHA = 0x303,
|
||||
DST_ALPHA = 0x304,
|
||||
ONE_MINUS_DST_ALPHA = 0x305,
|
||||
DST_COLOR = 0x306,
|
||||
ONE_MINUS_DST_COLOR = 0x307,
|
||||
SRC_ALPHA_SATURATE = 0x308,
|
||||
CONSTANT_COLOR = 0x8001,
|
||||
ONE_MINUS_CONSTANT_COLOR = 0x8002,
|
||||
CONSTANT_ALPHA = 0x8003,
|
||||
ONE_MINUS_CONSTANT_ALPHA = 0x8004
|
||||
}
|
||||
|
||||
public enum BlendingEquation
|
||||
{
|
||||
FUNC_ADD = 0x8006,
|
||||
FUNC_SUBTRACT = 0x800A,
|
||||
FUNC_REVERSE_SUBTRACT = 0x800B
|
||||
}
|
||||
|
||||
public enum Parameter
|
||||
{
|
||||
BLEND_EQUATION = 0x8009,
|
||||
BLEND_EQUATION_RGB = 0x8009,
|
||||
BLEND_EQUATION_ALPHA = 0x883D,
|
||||
BLEND_DST_RGB = 0x80C8,
|
||||
BLEND_SRC_RBG = 0x80C9,
|
||||
BLEND_DST_ALPHA = 0x80CA,
|
||||
BLEND_SRC_ALPHA = 0x80CB,
|
||||
BLEND_COLOR = 0x8005,
|
||||
ARRAY_BUFFER_BINDING = 0x8894,
|
||||
ELEMENT_ARRAY_BUFFER_BINDING = 0x8895,
|
||||
LINE_WIDTH = 0x0B21,
|
||||
ALIASED_POINT_SIZE_RANGE = 0x846D,
|
||||
ALIASED_LINE_WIDTH_RANGE = 0x846E,
|
||||
CULL_FACE_MODE = 0x0B45,
|
||||
FRONT_FACE = 0x0B46,
|
||||
DEPTH_RANGE = 0x0B70,
|
||||
DEPTH_WRITEMASK = 0x0B72,
|
||||
DEPTH_CLEAR_VALUE = 0x0B73,
|
||||
DEPTH_FUNC = 0x0B74,
|
||||
STENCIL_CLEAR_VALUE = 0x0B91,
|
||||
STENCIL_FUNC = 0x0B92,
|
||||
STENCIL_FAIL = 0x0B94,
|
||||
STENCIL_PASS_DEPTH_FAIL = 0x0B95,
|
||||
STENCIL_PASS_DEPTH_PASS = 0x0B96,
|
||||
STENCIL_REF = 0x0B97,
|
||||
STENCIL_VALUE_MASK = 0x0B93,
|
||||
STENCIL_WRITEMASK = 0x0B98,
|
||||
STENCIL_BACK_FUNC = 0x8800,
|
||||
STENCIL_BACK_FAIL = 0x8801,
|
||||
STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802,
|
||||
STENCIL_BACK_PASS_DEPTH_PASS = 0x8803,
|
||||
STENCIL_BACK_REF = 0x8CA3,
|
||||
STENCIL_BACK_VALUE_MASK = 0x8CA4,
|
||||
STENCIL_BACK_WRITEMASK = 0x8CA5,
|
||||
VIEWPORT = 0x0BA2,
|
||||
SCISSOR_BOX = 0x0C10,
|
||||
COLOR_CLEAR_VALUE = 0x0C22,
|
||||
COLOR_WRITEMASK = 0x0C23,
|
||||
UNPACK_ALIGNMENT = 0x0CF5,
|
||||
PACK_ALIGNMENT = 0x0D05,
|
||||
MAX_TEXTURE_SIZE = 0x0D33,
|
||||
MAX_VIEWPORT_DIMS = 0x0D3A,
|
||||
SUBPIXEL_BITS = 0x0D50,
|
||||
RED_BITS = 0x0D52,
|
||||
GREEN_BITS = 0x0D53,
|
||||
BLUE_BITS = 0x0D54,
|
||||
ALPHA_BITS = 0x0D55,
|
||||
DEPTH_BITS = 0x0D56,
|
||||
STENCIL_BITS = 0x0D57,
|
||||
POLYGON_OFFSET_UNITS = 0x2A00,
|
||||
POLYGON_OFFSET_FACTOR = 0x8038,
|
||||
TEXTURE_BINDING_2D = 0x8069,
|
||||
SAMPLE_BUFFERS = 0x80A8,
|
||||
SAMPLES = 0x80A9,
|
||||
SAMPLE_COVERAGE_VALUE = 0x80AA,
|
||||
SAMPLE_COVERAGE_INVERT = 0x80AB,
|
||||
COMPRESSED_TEXTURE_FORMATS = 0x86A3,
|
||||
VENDOR = 0x1F00,
|
||||
RENDERER = 0x1F01,
|
||||
VERSION = 0x1F02,
|
||||
IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A,
|
||||
IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B,
|
||||
BROWSER_DEFAULT_WEBGL = 0x9244,
|
||||
MAX_VERTEX_ATTRIBS = 0x8869,
|
||||
MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB,
|
||||
MAX_VARYING_VECTORS = 0x8DFC,
|
||||
MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D,
|
||||
MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C,
|
||||
MAX_TEXTURE_IMAGE_UNITS = 0x8872,
|
||||
MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD,
|
||||
SHADING_LANGUAGE_VERSION = 0x8B8C,
|
||||
CURRENT_PROGRAM = 0x8B8D,
|
||||
MAX_RENDERBUFFER_SIZE = 0x84E8,
|
||||
FRAMEBUFFER_BINDING = 0x8CA6,
|
||||
RENDERBUFFER_BINDING = 0x8CA7,
|
||||
ACTIVE_TEXTURE = 0x84E0
|
||||
}
|
||||
|
||||
public enum BufferUsageHint
|
||||
{
|
||||
STATIC_DRAW = 0x88E4,
|
||||
STREAM_DRAW = 0x88E0,
|
||||
DYNAMIC_DRAW = 0x88E8
|
||||
}
|
||||
|
||||
public enum BufferType
|
||||
{
|
||||
ARRAY_BUFFER = 0x8892,
|
||||
ELEMENT_ARRAY_BUFFER = 0x8893
|
||||
}
|
||||
|
||||
public enum BufferParameter
|
||||
{
|
||||
BUFFER_SIZE = 0x8764,
|
||||
BUFFER_USAGE = 0x8765
|
||||
}
|
||||
|
||||
public enum VertexAttribute
|
||||
{
|
||||
CURRENT_VERTEX_ATTRIB = 0x8626,
|
||||
VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622,
|
||||
VERTEX_ATTRIB_ARRAY_SIZE = 0x8623,
|
||||
VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624,
|
||||
VERTEX_ATTRIB_ARRAY_TYPE = 0x8625,
|
||||
VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A,
|
||||
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F
|
||||
}
|
||||
|
||||
public enum VertexAttributePointer
|
||||
{
|
||||
VERTEX_ATTRIB_ARRAY_POINTER = 0x8645
|
||||
}
|
||||
|
||||
public enum Face
|
||||
{
|
||||
FRONT = 0x0404,
|
||||
BACK = 0x0405,
|
||||
FRONT_AND_BACK = 0x0408
|
||||
}
|
||||
|
||||
public enum EnableCap
|
||||
{
|
||||
CULL_FACE = 0x0B44,
|
||||
BLEND = 0x0BE2,
|
||||
DEPTH_TEST = 0x0B71,
|
||||
DITHER = 0x0BD0,
|
||||
POLYGON_OFFSET_FILL = 0x8037,
|
||||
SAMPLE_ALPHA_TO_COVERAGE = 0x809E,
|
||||
SAMPLE_COVERAGE = 0x80A0,
|
||||
SCISSOR_TEST = 0x0C11,
|
||||
STENCIL_TEST = 0x0B90
|
||||
}
|
||||
|
||||
public enum Error
|
||||
{
|
||||
NO_ERROR = 0,
|
||||
INVALID_ENUM = 0x0500,
|
||||
INVALID_VALUE = 0x0501,
|
||||
INVALID_OPERATION = 0x0502,
|
||||
OUT_OF_MEMORY = 0x0505,
|
||||
CONTEXT_LOST_WEBGL = 0x9242
|
||||
}
|
||||
|
||||
public enum FrontFaceDirection
|
||||
{
|
||||
CW = 0x0900,
|
||||
CCW = 0x0901
|
||||
}
|
||||
|
||||
public enum HintMode
|
||||
{
|
||||
DONT_CARE = 0x1100,
|
||||
FASTEST = 0x1101,
|
||||
NICEST = 0x1102
|
||||
}
|
||||
|
||||
public enum HintTarget
|
||||
{
|
||||
GENERATE_MIPMAP_HINT = 0x8192
|
||||
}
|
||||
|
||||
public enum PixelFormat
|
||||
{
|
||||
ALPHA = 0x1906,
|
||||
RGB = 0x1907,
|
||||
RGBA = 0x1908,
|
||||
LUMINANCE = 0x1909,
|
||||
LUMINANCE_ALPHA = 0x190A
|
||||
}
|
||||
|
||||
public enum PixelType
|
||||
{
|
||||
UNSIGNED_BYTE = 0x1401,
|
||||
UNSIGNED_SHORT_4_4_4_4 = 0x8033,
|
||||
UNSIGNED_SHORT_5_5_5_1 = 0x8034,
|
||||
UNSIGNED_SHORT_5_6_5 = 0x8363,
|
||||
FLOAT = 0x1406
|
||||
}
|
||||
|
||||
public enum ShaderType
|
||||
{
|
||||
FRAGMENT_SHADER = 0x8B30,
|
||||
VERTEX_SHADER = 0x8B31
|
||||
}
|
||||
|
||||
public enum ShaderParameter
|
||||
{
|
||||
COMPILE_STATUS = 0x8B81,
|
||||
DELETE_STATUS = 0x8B80,
|
||||
SHADER_TYPE = 0x8B4F
|
||||
}
|
||||
|
||||
public enum ProgramParameter
|
||||
{
|
||||
DELETE_STATUS = 0x8B80,
|
||||
LINK_STATUS = 0x8B82,
|
||||
VALIDATE_STATUS = 0x8B83,
|
||||
ATTACHED_SHADERS = 0x8B85,
|
||||
ACTIVE_ATTRIBUTES = 0x8B89,
|
||||
ACTIVE_UNIFORMS = 0x8B86
|
||||
}
|
||||
|
||||
public enum CompareFunction
|
||||
{
|
||||
NEVER = 0x0200,
|
||||
ALWAYS = 0x0207,
|
||||
LESS = 0x0201,
|
||||
EQUAL = 0x0202,
|
||||
LEQUAL = 0x0203,
|
||||
GREATER = 0x0204,
|
||||
GEQUAL = 0x0206,
|
||||
NOTEQUAL = 0x0205
|
||||
}
|
||||
|
||||
public enum StencilFunction
|
||||
{
|
||||
KEEP = 0x1E00,
|
||||
REPLACE = 0x1E01,
|
||||
INCR = 0x1E02,
|
||||
DECR = 0x1E03,
|
||||
INVERT = 0x150A,
|
||||
INCR_WRAP = 0x8507,
|
||||
DECR_WRAP = 0x8508
|
||||
}
|
||||
|
||||
public enum TextureType
|
||||
{
|
||||
TEXTURE_2D = 0x0DE1,
|
||||
TEXTURE_CUBE_MAP = 0x8513,
|
||||
}
|
||||
|
||||
public enum Texture2DType
|
||||
{
|
||||
TEXTURE_2D = 0x0DE1,
|
||||
TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515,
|
||||
TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516,
|
||||
TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517,
|
||||
TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518,
|
||||
TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519,
|
||||
TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A
|
||||
}
|
||||
|
||||
public enum TextureParameter
|
||||
{
|
||||
TEXTURE_MAG_FILTER = 0x2800,
|
||||
TEXTURE_MIN_FILTER = 0x2801,
|
||||
TEXTURE_WRAP_S = 0x2802,
|
||||
TEXTURE_WRAP_T = 0x2803
|
||||
}
|
||||
|
||||
public enum TextureParameterValue
|
||||
{
|
||||
NEAREST = 0x2600,
|
||||
LINEAR = 0x2601,
|
||||
NEAREST_MIPMAP_NEAREST = 0x2700,
|
||||
LINEAR_MIPMAP_NEAREST = 0x2701,
|
||||
NEAREST_MIPMAP_LINEAR = 0x2702,
|
||||
LINEAR_MIPMAP_LINEAR = 0x2703,
|
||||
REPEAT = 0x2901,
|
||||
CLAMP_TO_EDGE = 0x812F,
|
||||
MIRRORED_REPEAT = 0x8370
|
||||
}
|
||||
|
||||
public enum UniformType
|
||||
{
|
||||
FLOAT_VEC2 = 0x8B50,
|
||||
FLOAT_VEC3 = 0x8B51,
|
||||
FLOAT_VEC4 = 0x8B52,
|
||||
INT_VEC2 = 0x8B53,
|
||||
INT_VEC3 = 0x8B54,
|
||||
INT_VEC4 = 0x8B55,
|
||||
BOOL = 0x8B56,
|
||||
BOOL_VEC2 = 0x8B57,
|
||||
BOOL_VEC3 = 0x8B58,
|
||||
BOOL_VEC4 = 0x8B59,
|
||||
FLOAT_MAT2 = 0x8B5A,
|
||||
FLOAT_MAT3 = 0x8B5B,
|
||||
FLOAT_MAT4 = 0x8B5C,
|
||||
SAMPLER_2D = 0x8B5E,
|
||||
SAMPLER_CUBE = 0x8B60
|
||||
}
|
||||
|
||||
public enum ShaderPrecision
|
||||
{
|
||||
LOW_FLOAT = 0x8DF0,
|
||||
MEDIUM_FLOAT = 0x8DF1,
|
||||
HIGH_FLOAT = 0x8DF2,
|
||||
LOW_INT = 0x8DF3,
|
||||
MEDIUM_INT = 0x8DF4,
|
||||
HIGH_INT = 0x8DF5
|
||||
}
|
||||
|
||||
public enum RenderbufferType
|
||||
{
|
||||
RENDERBUFFER = 0x8D41
|
||||
}
|
||||
|
||||
public enum FramebufferType
|
||||
{
|
||||
FRAMEBUFFER = 0x8D40
|
||||
}
|
||||
|
||||
public enum RenderbufferParameter
|
||||
{
|
||||
RENDERBUFFER_WIDTH = 0x8D42,
|
||||
RENDERBUFFER_HEIGHT = 0x8D43,
|
||||
RENDERBUFFER_INTERNAL_FORMAT = 0x8D44,
|
||||
RENDERBUFFER_RED_SIZE = 0x8D50,
|
||||
RENDERBUFFER_GREEN_SIZE = 0x8D51,
|
||||
RENDERBUFFER_BLUE_SIZE = 0x8D52,
|
||||
RENDERBUFFER_ALPHA_SIZE = 0x8D53,
|
||||
RENDERBUFFER_DEPTH_SIZE = 0x8D54,
|
||||
RENDERBUFFER_STENCIL_SIZE = 0x8D55
|
||||
}
|
||||
|
||||
public enum FramebufferAttachment
|
||||
{
|
||||
COLOR_ATTACHMENT0 = 0x8CE0,
|
||||
DEPTH_ATTACHMENT = 0x8D00,
|
||||
STENCIL_ATTACHMENT = 0x8D20,
|
||||
DEPTH_STENCIL_ATTACHMENT = 0x821A
|
||||
}
|
||||
|
||||
public enum FramebufferAttachmentParameter
|
||||
{
|
||||
FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0,
|
||||
FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1,
|
||||
FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2,
|
||||
FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3
|
||||
}
|
||||
|
||||
public enum FramebufferStatus
|
||||
{
|
||||
NONE = 0,
|
||||
FRAMEBUFFER_COMPLETE = 0x8CD5,
|
||||
FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6,
|
||||
FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7,
|
||||
FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9,
|
||||
FRAMEBUFFER_UNSUPPORTED = 0x8CDD
|
||||
}
|
||||
|
||||
public enum PixelStorageMode
|
||||
{
|
||||
UNPACK_FLIP_Y_WEBGL = 0x9240,
|
||||
UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
|
||||
UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243
|
||||
}
|
||||
|
||||
public enum RenderbufferFormat
|
||||
{
|
||||
RGBA4 = 0x8056,
|
||||
RGB5_A1 = 0x8057,
|
||||
RGB565 = 0x8D62,
|
||||
DEPTH_COMPONENT16 = 0x81A5,
|
||||
STENCIL_INDEX8 = 0x8D48,
|
||||
DEPTH_STENCIL = 0x84F9
|
||||
}
|
||||
|
||||
public enum DataType
|
||||
{
|
||||
BYTE = 0x1400,
|
||||
UNSIGNED_BYTE = 0x1401,
|
||||
SHORT = 0x1402,
|
||||
UNSIGNED_SHORT = 0x1403,
|
||||
INT = 0x1404,
|
||||
UNSIGNED_INT = 0x1405,
|
||||
FLOAT = 0x1406
|
||||
}
|
||||
|
||||
public enum Texture
|
||||
{
|
||||
TEXTURE0 = 0x84C0,
|
||||
TEXTURE1,
|
||||
TEXTURE2,
|
||||
TEXTURE3,
|
||||
TEXTURE4,
|
||||
TEXTURE5,
|
||||
TEXTURE6,
|
||||
TEXTURE7,
|
||||
TEXTURE8,
|
||||
TEXTURE9,
|
||||
TEXTURE10,
|
||||
TEXTURE11,
|
||||
TEXTURE12,
|
||||
TEXTURE13,
|
||||
TEXTURE14,
|
||||
TEXTURE15,
|
||||
TEXTURE16,
|
||||
TEXTURE17,
|
||||
TEXTURE18,
|
||||
TEXTURE19,
|
||||
TEXTURE20,
|
||||
TEXTURE21,
|
||||
TEXTURE22,
|
||||
TEXTURE23,
|
||||
TEXTURE24,
|
||||
TEXTURE25,
|
||||
TEXTURE26,
|
||||
TEXTURE27,
|
||||
TEXTURE28,
|
||||
TEXTURE29,
|
||||
TEXTURE30,
|
||||
TEXTURE31
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
namespace System.Graphics.Blazor.WebGL
|
||||
{
|
||||
public class WebGLContextAttributes
|
||||
{
|
||||
public bool Alpha { get; set; } = true;
|
||||
public bool Antialias { get; set; } = true;
|
||||
public bool Depth { get; set; } = true;
|
||||
public bool PremultipliedAlpha { get; set; } = true;
|
||||
public bool PreserveDrawingBuffer { get; set; } = false;
|
||||
public bool Stencil { get; set; } = false;
|
||||
public string PowerPreference { get; set; } = POWER_PREFERENCE_DEFAULT;
|
||||
public bool FailIfMajorPerformanceCaveat { get; set; } = false;
|
||||
|
||||
public const string POWER_PREFERENCE_DEFAULT = "default";
|
||||
public const string POWER_PREFERENCE_HIGH_PERFORMANCE = "high-performance";
|
||||
public const string POWER_PREFERENCE_LOW_POWER = "low-power";
|
||||
}
|
||||
|
||||
public class WebGLShaderPrecisionFormat
|
||||
{
|
||||
public int RangeMin { get; set; }
|
||||
public int RangeMax { get; set; }
|
||||
public int Precision { get; set; }
|
||||
}
|
||||
|
||||
public class WebGLActiveInfo
|
||||
{
|
||||
public string Name { get; set; } //todo: make readonly
|
||||
public int Size { get; set; }
|
||||
public UniformType Type { get; set; }
|
||||
}
|
||||
|
||||
public class WebGLObject
|
||||
{
|
||||
public string WebGLType { get; set; }
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class WebGLBuffer : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLFramebuffer : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLRenderbuffer : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLTexture : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLProgram : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLShader : WebGLObject
|
||||
{ }
|
||||
|
||||
public class WebGLUniformLocation : WebGLObject
|
||||
{ }
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
window.EWGraphicsInterop = {
|
||||
SetupCanvas: function (id)
|
||||
{
|
||||
var canvas = document.getElementById(id);
|
||||
if (canvas)
|
||||
{
|
||||
// Get the device pixel ratio, falling back to 1.
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
// Get the size of the canvas in CSS pixels.
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
// Give the canvas pixel dimensions of their CSS
|
||||
// size * the device pixel ratio.
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = rect.height * dpr;
|
||||
var ctx = canvas.getContext('2d');
|
||||
// Scale all drawing operations by the dpr, so you
|
||||
// don't have to worry about the difference.
|
||||
ctx.scale(dpr, dpr);
|
||||
return dpr;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
PointIsInPath : function (path, x, y)
|
||||
{
|
||||
var canvas = document.getElementsByTagName("canvas")[0];
|
||||
if (canvas)
|
||||
{
|
||||
var path2d = new Path2D(path);
|
||||
return canvas.isPointInPath(path, x, y);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 378 B |
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
This file is to show how CSS and other static resources (such as images) can be
|
||||
used from a library project/package.
|
||||
*/
|
||||
|
||||
.my-component {
|
||||
border: 2px dashed red;
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
background-image: url('background.png');
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Graphics.Text;
|
||||
|
||||
namespace System.Graphics
|
||||
{
|
||||
public abstract class AbstractCanvas<TState> : ICanvas, IDisposable where TState : CanvasState
|
||||
{
|
||||
private readonly Stack<TState> _stateStack = new Stack<TState>();
|
||||
private readonly Func<object, TState> _createNew;
|
||||
private readonly Func<TState, TState> _createCopy;
|
||||
|
||||
private TState _currentState;
|
||||
private bool _limitStrokeScaling;
|
||||
private float _strokeLimit = 1;
|
||||
private bool _strokeDashPatternDirty;
|
||||
|
||||
protected abstract float NativeStrokeSize { set; }
|
||||
protected abstract void NativeSetStrokeDashPattern(float[] pattern, float strokeSize);
|
||||
protected abstract void NativeDrawLine(float x1, float y1, float x2, float y2);
|
||||
protected abstract void NativeDrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed);
|
||||
protected abstract void NativeDrawRectangle(float x, float y, float width, float height);
|
||||
protected abstract void NativeDrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius);
|
||||
protected abstract void NativeDrawOval(float x, float y, float width, float height);
|
||||
protected abstract void NativeDrawPath(PathF path);
|
||||
protected abstract void NativeRotate(float degrees, float radians, float x, float y);
|
||||
protected abstract void NativeRotate(float degrees, float radians);
|
||||
protected abstract void NativeScale(float fx, float fy);
|
||||
protected abstract void NativeTranslate(float tx, float ty);
|
||||
protected abstract void NativeConcatenateTransform(AffineTransform transform);
|
||||
|
||||
protected AbstractCanvas(Func<object, TState> createNew, Func<TState, TState> createCopy)
|
||||
{
|
||||
_createCopy = createCopy;
|
||||
_createNew = createNew;
|
||||
_currentState = createNew(this);
|
||||
}
|
||||
|
||||
protected TState CurrentState => _currentState;
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (_currentState != null)
|
||||
{
|
||||
_currentState.Dispose();
|
||||
_currentState = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool LimitStrokeScaling
|
||||
{
|
||||
set => _limitStrokeScaling = value;
|
||||
}
|
||||
|
||||
protected bool LimitStrokeScalingEnabled => _limitStrokeScaling;
|
||||
|
||||
public float StrokeLimit
|
||||
{
|
||||
set => _strokeLimit = value;
|
||||
}
|
||||
|
||||
public abstract Color FillColor { set; }
|
||||
public abstract Color FontColor { set; }
|
||||
public abstract string FontName { set; }
|
||||
public abstract float FontSize { set; }
|
||||
public abstract float Alpha { set; }
|
||||
public abstract bool Antialias { set; }
|
||||
public abstract BlendMode BlendMode { set; }
|
||||
|
||||
protected float AssignedStrokeLimit => _strokeLimit;
|
||||
|
||||
public virtual float DisplayScale { get; set; }
|
||||
|
||||
public float RetinaScale { get; set; }
|
||||
|
||||
public float StrokeSize
|
||||
{
|
||||
set
|
||||
{
|
||||
var size = value;
|
||||
|
||||
if (_limitStrokeScaling)
|
||||
{
|
||||
var scale = _currentState.Scale;
|
||||
var scaledStrokeSize = scale * value;
|
||||
if (scaledStrokeSize < _strokeLimit)
|
||||
{
|
||||
size = _strokeLimit / scale;
|
||||
}
|
||||
}
|
||||
|
||||
_currentState.StrokeSize = size;
|
||||
NativeStrokeSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract float MiterLimit { set; }
|
||||
public abstract Color StrokeColor { set; }
|
||||
public abstract LineCap StrokeLineCap { set; }
|
||||
public abstract LineJoin StrokeLineJoin { set; }
|
||||
|
||||
public float[] StrokeDashPattern
|
||||
{
|
||||
set
|
||||
{
|
||||
if (!ReferenceEquals(value, _currentState.StrokeDashPattern))
|
||||
{
|
||||
_currentState.StrokeDashPattern = value;
|
||||
_strokeDashPatternDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureStrokePatternSet()
|
||||
{
|
||||
if (_strokeDashPatternDirty)
|
||||
{
|
||||
NativeSetStrokeDashPattern(_currentState.StrokeDashPattern, _currentState.StrokeSize);
|
||||
_strokeDashPatternDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ClipRectangle(float x, float y, float width, float height);
|
||||
|
||||
public void DrawLine(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawLine(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
public void DrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)
|
||||
{
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawArc(x, y, width, height, startAngle, endAngle, clockwise, closed);
|
||||
}
|
||||
|
||||
public abstract void FillArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise);
|
||||
|
||||
public void DrawRectangle(float x, float y, float width, float height)
|
||||
{
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawRectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
public abstract void FillRectangle(float x, float y, float width, float height);
|
||||
|
||||
public void DrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
|
||||
{
|
||||
var halfHeight = Math.Abs(height / 2);
|
||||
if (cornerRadius > halfHeight)
|
||||
cornerRadius = halfHeight;
|
||||
|
||||
var halfWidth = Math.Abs(width / 2);
|
||||
if (cornerRadius > halfWidth)
|
||||
cornerRadius = halfWidth;
|
||||
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawRoundedRectangle(x, y, width, height, cornerRadius);
|
||||
}
|
||||
|
||||
public abstract void FillRoundedRectangle(float x, float y, float width, float height, float cornerRadius);
|
||||
|
||||
public void DrawOval(float x, float y, float width, float height)
|
||||
{
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawOval(x, y, width, height);
|
||||
}
|
||||
|
||||
public abstract void FillOval(float x, float y, float width, float height);
|
||||
public abstract void DrawString(string value, float x, float y, HorizontalAlignment horizontalAlignment);
|
||||
|
||||
public abstract void DrawString(
|
||||
string value,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
HorizontalAlignment horizontalAlignment,
|
||||
VerticalAlignment verticalAlignment,
|
||||
TextFlow textFlow = TextFlow.ClipBounds,
|
||||
float lineSpacingAdjustment = 0);
|
||||
|
||||
public abstract void DrawText(IAttributedText value, float x, float y, float width, float height);
|
||||
|
||||
public void DrawPath(PathF path)
|
||||
{
|
||||
EnsureStrokePatternSet();
|
||||
NativeDrawPath(path);
|
||||
}
|
||||
|
||||
public abstract void FillPath(PathF path, WindingMode windingMode);
|
||||
public abstract void SubtractFromClip(float x, float y, float width, float height);
|
||||
public abstract void ClipPath(PathF path, WindingMode windingMode = WindingMode.NonZero);
|
||||
|
||||
public virtual void ResetState()
|
||||
{
|
||||
while (_stateStack.Count > 0)
|
||||
{
|
||||
if (_currentState != null)
|
||||
{
|
||||
_currentState.Dispose();
|
||||
_currentState = null;
|
||||
}
|
||||
|
||||
_currentState = _stateStack.Pop();
|
||||
StateRestored(_currentState);
|
||||
}
|
||||
|
||||
if (_currentState != null)
|
||||
{
|
||||
_currentState.Dispose();
|
||||
_currentState = null;
|
||||
}
|
||||
|
||||
_currentState = _createNew(this);
|
||||
}
|
||||
|
||||
public abstract void SetShadow(SizeF offset, float blur, Color color);
|
||||
public abstract void SetFillPaint(Paint paint, float x1, float y1, float x2, float y2);
|
||||
public abstract void SetToSystemFont();
|
||||
public abstract void SetToBoldSystemFont();
|
||||
public abstract void DrawImage(IImage image, float x, float y, float width, float height);
|
||||
|
||||
public virtual bool RestoreState()
|
||||
{
|
||||
_strokeDashPatternDirty = true;
|
||||
|
||||
if (_currentState != null)
|
||||
{
|
||||
_currentState.Dispose();
|
||||
_currentState = null;
|
||||
}
|
||||
|
||||
if (_stateStack.Count > 0)
|
||||
{
|
||||
_currentState = _stateStack.Pop();
|
||||
StateRestored(_currentState);
|
||||
return true;
|
||||
}
|
||||
|
||||
_currentState = _createNew(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void StateRestored(TState state)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public virtual void SaveState()
|
||||
{
|
||||
var previousState = _currentState;
|
||||
_stateStack.Push(previousState);
|
||||
_currentState = _createCopy(previousState);
|
||||
_strokeDashPatternDirty = true;
|
||||
}
|
||||
|
||||
public void Rotate(float degrees, float x, float y)
|
||||
{
|
||||
var radians = Geometry.DegreesToRadians(degrees);
|
||||
|
||||
var transform = _currentState.Transform;
|
||||
transform.Translate(x, y);
|
||||
transform.Rotate(radians);
|
||||
transform.Translate(-x, -y);
|
||||
|
||||
NativeRotate(degrees, radians, x, y);
|
||||
}
|
||||
|
||||
public void Rotate(float degrees)
|
||||
{
|
||||
var radians = Geometry.DegreesToRadians(degrees);
|
||||
_currentState.Transform.Rotate(radians);
|
||||
|
||||
NativeRotate(degrees, radians);
|
||||
}
|
||||
|
||||
public void Scale(float fx, float fy)
|
||||
{
|
||||
_currentState.Scale *= fx;
|
||||
_currentState.Transform.Scale(fx, fy);
|
||||
|
||||
NativeScale(fx, fy);
|
||||
}
|
||||
|
||||
public void Translate(float tx, float ty)
|
||||
{
|
||||
_currentState.Transform.Translate(tx, ty);
|
||||
NativeTranslate(tx, ty);
|
||||
}
|
||||
|
||||
public void ConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
_currentState.Scale *= transform.ScaleX;
|
||||
_currentState.Transform.Concatenate(transform);
|
||||
NativeConcatenateTransform(transform);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Graphics
|
||||
{
|
||||
public abstract class AbstractFontService : IFontService
|
||||
{
|
||||
private readonly string _defaultFontStyleName;
|
||||
private readonly string _secondaryFontStyleName;
|
||||
private readonly string _tertiaryFontStyleName;
|
||||
|
||||
private readonly Dictionary<string, IFontStyle> _fontCache = new Dictionary<string, IFontStyle>();
|
||||
private IFontStyle _defaultFontStyle;
|
||||
private bool _buildingDefaultStyle;
|
||||
|
||||
public abstract IFontFamily[] GetFontFamilies();
|
||||
|
||||
protected AbstractFontService(
|
||||
string defaultFontStyleName = "Arial",
|
||||
string secondaryFontStyleName = "Helvetica",
|
||||
string tertiaryFontStyleName = null)
|
||||
{
|
||||
_defaultFontStyleName = defaultFontStyleName;
|
||||
_secondaryFontStyleName = secondaryFontStyleName;
|
||||
_tertiaryFontStyleName = tertiaryFontStyleName;
|
||||
}
|
||||
|
||||
public virtual IFontStyle GetDefaultFontStyle()
|
||||
{
|
||||
if (_defaultFontStyle == null)
|
||||
{
|
||||
if (_buildingDefaultStyle)
|
||||
return null;
|
||||
|
||||
_buildingDefaultStyle = true;
|
||||
_defaultFontStyle = GetFontStyleById(_defaultFontStyleName);
|
||||
|
||||
if (_defaultFontStyle == null && !string.IsNullOrEmpty(_secondaryFontStyleName))
|
||||
{
|
||||
_defaultFontStyle = GetFontStyleById(_secondaryFontStyleName);
|
||||
}
|
||||
|
||||
if (_defaultFontStyle == null && !string.IsNullOrEmpty(_tertiaryFontStyleName))
|
||||
{
|
||||
_defaultFontStyle = GetFontStyleById(_tertiaryFontStyleName);
|
||||
}
|
||||
|
||||
if (_defaultFontStyle == null)
|
||||
{
|
||||
var families = GetFontFamilies();
|
||||
if (families != null && families.Length > 0)
|
||||
{
|
||||
_defaultFontStyle = families[0].GetFontStyles()[0];
|
||||
}
|
||||
}
|
||||
|
||||
_buildingDefaultStyle = false;
|
||||
}
|
||||
|
||||
return _defaultFontStyle;
|
||||
}
|
||||
|
||||
public IFontStyle GetFontStyleById(string id)
|
||||
{
|
||||
if (id == null)
|
||||
return null;
|
||||
|
||||
if (!_fontCache.TryGetValue(id, out var fontStyle))
|
||||
{
|
||||
var families = GetFontFamilies();
|
||||
for (int f = 0; f < families.Length && fontStyle == null; f++)
|
||||
{
|
||||
var family = families[f];
|
||||
|
||||
if (id.Equals(family.Name))
|
||||
{
|
||||
fontStyle = family.GetFontStyles()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
var styles = family.GetFontStyles();
|
||||
if (styles != null)
|
||||
{
|
||||
for (int s = 0; s < styles.Length && fontStyle == null; s++)
|
||||
{
|
||||
var style = styles[s];
|
||||
if (id.Equals(style.Id, StringComparison.OrdinalIgnoreCase))
|
||||
fontStyle = style;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fontStyle != null)
|
||||
_fontCache[id] = fontStyle;
|
||||
else
|
||||
_fontCache[id] = GetDefaultFontStyle();
|
||||
}
|
||||
|
||||
return fontStyle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
namespace System.Graphics
|
||||
{
|
||||
public abstract class AbstractPattern : IPattern
|
||||
{
|
||||
public float Width { get; }
|
||||
public float Height { get; }
|
||||
public float StepX { get; }
|
||||
public float StepY { get; }
|
||||
|
||||
protected AbstractPattern(float width, float height, float stepX, float stepY)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
StepX = stepX;
|
||||
StepY = stepY;
|
||||
}
|
||||
|
||||
protected AbstractPattern(float width, float height) : this(width, height, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
protected AbstractPattern(float stepSize) : this(stepSize, stepSize)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Draw(ICanvas canvas);
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче