├── .gitattributes ├── .gitignore ├── .gitmodules ├── Demo.Desktop ├── CMakeLists.txt ├── DeclareDPIAware.manifest ├── Demo.Desktop.vcxproj ├── Demo.Desktop.vcxproj.filters └── main.c ├── LICENSE.txt ├── README.md ├── WTFDanmaku.sln ├── include ├── WTFDanmaku.h ├── WTFEngine.hpp ├── WTFWindow.hpp └── WTF_API.h ├── libwtfdanmaku.Desktop ├── libwtfdanmaku.Desktop.vcxproj └── libwtfdanmaku.Desktop.vcxproj.filters ├── libwtfdanmaku.Universal ├── libwtfdanmaku.Universal.vcxproj └── libwtfdanmaku.Universal.vcxproj.filters ├── libwtfdanmaku.Windows ├── libwtfdanmaku.Windows.vcxproj └── libwtfdanmaku.Windows.vcxproj.filters ├── libwtfdanmaku.Windows7 ├── libwtfdanmaku.Windows7.vcxproj └── libwtfdanmaku.Windows7.vcxproj.filters ├── libwtfdanmaku.WindowsPhone ├── libwtfdanmaku.WindowsPhone.vcxproj └── libwtfdanmaku.WindowsPhone.vcxproj.filters └── src ├── BaseDanmaku.cpp ├── BaseDanmaku.hpp ├── BilibiliParser.cpp ├── BilibiliParser.hpp ├── BottomDanmaku.cpp ├── BottomDanmaku.hpp ├── Controller.cpp ├── Controller.hpp ├── DanmakuConfig.hpp ├── DanmakuFactory.cpp ├── DanmakuFactory.hpp ├── DanmakusManager.cpp ├── DanmakusManager.hpp ├── DanmakusRetainer.cpp ├── DanmakusRetainer.hpp ├── Displayer.cpp ├── Displayer.hpp ├── DisplayerImpl.cpp ├── DisplayerImpl.hpp ├── IParser.hpp ├── ITimer.hpp ├── Noncopyable.hpp ├── OutlineTextRenderer.cpp ├── OutlineTextRenderer.hpp ├── PerformanceTimer.cpp ├── PerformanceTimer.hpp ├── R2LDanmaku.cpp ├── R2LDanmaku.hpp ├── Rect.hpp ├── Renderable.cpp ├── Renderable.hpp ├── StringUtils.cpp ├── StringUtils.hpp ├── TopDanmaku.cpp ├── TopDanmaku.hpp ├── WTFDanmaku.cpp ├── WTFEngine.cpp ├── WTFWindow.cpp ├── Win32Mutex.hpp ├── WinmmTimer.cpp ├── WinmmTimer.hpp ├── dllmain.cpp ├── libwtfdanmaku.Shared.vcxitems └── libwtfdanmaku.Shared.vcxitems.filters /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | build/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | 22 | # Roslyn cache directories 23 | *.ide/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | #NUNIT 30 | *.VisualState.xml 31 | TestResult.xml 32 | 33 | # Build Results of an ATL Project 34 | [Dd]ebugPS/ 35 | [Rr]eleasePS/ 36 | dlldata.c 37 | 38 | *_i.c 39 | *_p.c 40 | *_i.h 41 | *.ilk 42 | *.meta 43 | *.obj 44 | *.pch 45 | *.pdb 46 | *.pgc 47 | *.pgd 48 | *.rsp 49 | *.sbr 50 | *.tlb 51 | *.tli 52 | *.tlh 53 | *.tmp 54 | *.tmp_proj 55 | *.log 56 | *.vspscc 57 | *.vssscc 58 | .builds 59 | *.pidb 60 | *.svclog 61 | *.scc 62 | 63 | # Chutzpah Test files 64 | _Chutzpah* 65 | 66 | # Visual C++ cache files 67 | ipch/ 68 | *.aps 69 | *.ncb 70 | *.opensdf 71 | *.sdf 72 | *.cachefile 73 | 74 | # Visual Studio profiler 75 | *.psess 76 | *.vsp 77 | *.vspx 78 | 79 | # TFS 2012 Local Workspace 80 | $tf/ 81 | 82 | # Guidance Automation Toolkit 83 | *.gpState 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper*/ 87 | *.[Rr]e[Ss]harper 88 | *.DotSettings.user 89 | 90 | # JustCode is a .NET coding addin-in 91 | .JustCode 92 | 93 | # TeamCity is a build add-in 94 | _TeamCity* 95 | 96 | # DotCover is a Code Coverage Tool 97 | *.dotCover 98 | 99 | # NCrunch 100 | _NCrunch_* 101 | .*crunch*.local.xml 102 | 103 | # MightyMoose 104 | *.mm.* 105 | AutoTest.Net/ 106 | 107 | # Web workbench (sass) 108 | .sass-cache/ 109 | 110 | # Installshield output folder 111 | [Ee]xpress/ 112 | 113 | # DocProject is a documentation generator add-in 114 | DocProject/buildhelp/ 115 | DocProject/Help/*.HxT 116 | DocProject/Help/*.HxC 117 | DocProject/Help/*.hhc 118 | DocProject/Help/*.hhk 119 | DocProject/Help/*.hhp 120 | DocProject/Help/Html2 121 | DocProject/Help/html 122 | 123 | # Click-Once directory 124 | publish/ 125 | 126 | # Publish Web Output 127 | *.[Pp]ublish.xml 128 | *.azurePubxml 129 | # TODO: Comment the next line if you want to checkin your web deploy settings 130 | # but database connection strings (with potential passwords) will be unencrypted 131 | *.pubxml 132 | *.publishproj 133 | 134 | # NuGet Packages 135 | *.nupkg 136 | # The packages folder can be ignored because of Package Restore 137 | **/packages/* 138 | # except build/, which is used as an MSBuild target. 139 | !**/packages/build/ 140 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 141 | #!**/packages/repositories.config 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # ========================= 187 | # Operating System Files 188 | # ========================= 189 | 190 | # OSX 191 | # ========================= 192 | 193 | .DS_Store 194 | .AppleDouble 195 | .LSOverride 196 | 197 | # Thumbnails 198 | ._* 199 | 200 | # Files that might appear on external disk 201 | .Spotlight-V100 202 | .Trashes 203 | 204 | # Directories potentially created on remote AFP share 205 | .AppleDB 206 | .AppleDesktop 207 | Network Trash Folder 208 | Temporary Items 209 | .apdisk 210 | 211 | # Windows 212 | # ========================= 213 | 214 | # Windows image file caches 215 | Thumbs.db 216 | ehthumbs.db 217 | 218 | # Folder config file 219 | Desktop.ini 220 | 221 | # Recycle Bin used on file shares 222 | $RECYCLE.BIN/ 223 | 224 | # Windows Installer files 225 | *.cab 226 | *.msi 227 | *.msm 228 | *.msp 229 | 230 | # Windows shortcuts 231 | *.lnk 232 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/rapidxml"] 2 | path = 3rdparty/rapidxml 3 | url = https://github.com/xqq/rapidxml.git 4 | [submodule "src/base"] 5 | path = src/base 6 | url = https://github.com/xqq/SmartRef.git 7 | -------------------------------------------------------------------------------- /Demo.Desktop/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(demo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | include_directories(demo ../include) 7 | link_directories(demo ../Release/libwtfdanmaku.Desktop) 8 | 9 | set(SOURCE_FILES main.c) 10 | 11 | add_executable(demo ${SOURCE_FILES}) 12 | 13 | target_link_libraries(demo libwtfdanmaku) -------------------------------------------------------------------------------- /Demo.Desktop/DeclareDPIAware.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True/PM 5 | 6 | 7 | -------------------------------------------------------------------------------- /Demo.Desktop/Demo.Desktop.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134} 23 | Demo 24 | 25 | 26 | 27 | Application 28 | true 29 | v120 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | v120 36 | Unicode 37 | 38 | 39 | Application 40 | false 41 | v120 42 | true 43 | Unicode 44 | 45 | 46 | Application 47 | false 48 | v120 49 | true 50 | Unicode 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | $(SolutionDir)$(Configuration)\libwtfdanmaku.Desktop\ 70 | $(Configuration)\ 71 | $(RootNamespace) 72 | 73 | 74 | $(SolutionDir)$(Platform)\$(Configuration)\libwtfdanmaku.Desktop\ 75 | $(Platform)\$(Configuration)\ 76 | $(RootNamespace) 77 | 78 | 79 | $(SolutionDir)$(Platform)\$(Configuration)\libwtfdanmaku.Desktop\ 80 | $(Platform)\$(Configuration)\ 81 | $(RootNamespace) 82 | 83 | 84 | $(SolutionDir)$(Configuration)\libwtfdanmaku.Desktop\ 85 | $(Configuration)\ 86 | $(RootNamespace) 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | true 93 | $(SolutionDir)include\;%(AdditionalIncludeDirectories) 94 | 95 | 96 | true 97 | %(AdditionalDependencies) 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | true 105 | $(SolutionDir)include\;%(AdditionalIncludeDirectories) 106 | 107 | 108 | true 109 | %(AdditionalDependencies) 110 | 111 | 112 | 113 | 114 | Level3 115 | MaxSpeed 116 | true 117 | true 118 | true 119 | $(SolutionDir)include\;%(AdditionalIncludeDirectories) 120 | 121 | 122 | true 123 | true 124 | true 125 | %(AdditionalDependencies) 126 | 127 | 128 | 129 | 130 | Level3 131 | MaxSpeed 132 | true 133 | true 134 | true 135 | $(SolutionDir)include\;%(AdditionalIncludeDirectories) 136 | 137 | 138 | true 139 | true 140 | true 141 | %(AdditionalDependencies) 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | {817ebe08-2be4-4251-92d0-117822d8f16a} 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Demo.Desktop/Demo.Desktop.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Resource Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo.Desktop/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "WTFDanmaku.h" 6 | 7 | #ifndef WS_EX_NOREDIRECTIONBITMAP 8 | #define WS_EX_NOREDIRECTIONBITMAP 0x00200000L 9 | #endif 10 | 11 | LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 12 | 13 | bool hasArgFile = false; 14 | char optFileName[MAX_PATH] = { 0 }; 15 | bool wtfInited = false; 16 | WTF_Window* window = NULL; 17 | WTF_Instance* wtf = NULL; 18 | 19 | int main(int argc, char** argv) { 20 | if (argv[1]) { 21 | if (strlen(argv[1]) > 0) { 22 | strcpy_s(optFileName, sizeof(optFileName), argv[1]); 23 | hasArgFile = true; 24 | } 25 | } 26 | window = WTFWindow_Create(GetModuleHandle(NULL), SW_SHOWNORMAL); 27 | WTFWindow_SetCustomWndProc(window, WndProc); 28 | WTFWindow_Initialize(window, WS_EX_NOREDIRECTIONBITMAP, 1280, 720, L"WTFDanmaku Demo"); 29 | WTFWindow_SetHitTestOverEnabled(window, 0); 30 | int ret = WTFWindow_RunMessageLoop(window); 31 | WTFWindow_Release(window); 32 | return ret; 33 | } 34 | 35 | void InitializeWTF(HWND hwnd) { 36 | if (!wtfInited) { 37 | wtf = WTF_CreateInstance(); 38 | WTF_InitializeWithHwnd(wtf, (void*)hwnd); 39 | WTF_SetFontName(wtf, L"SimHei"); 40 | WTF_SetFontWeight(wtf, 700); 41 | WTF_SetFontScaleFactor(wtf, 1.0f); 42 | WTF_SetDanmakuStyle(wtf, WTF_DANMAKU_STYLE_OUTLINE); 43 | WTF_SetCompositionOpacity(wtf, 0.9f); 44 | WTF_SetDanmakuTypeVisibility(wtf, WTF_DANMAKU_TYPE_SCROLLING_VISIBLE | 45 | WTF_DANMAKU_TYPE_TOP_VISIBLE | 46 | WTF_DANMAKU_TYPE_BOTTOM_VISIBLE); 47 | 48 | if (hasArgFile) { 49 | WTF_LoadBilibiliFile(wtf, optFileName); 50 | } else { 51 | WTF_LoadBilibiliFile(wtf, "E:\\Downloads\\Downloads\\1055660.xml"); 52 | } 53 | wtfInited = true; 54 | } 55 | WTF_Start(wtf); 56 | } 57 | 58 | void ResizeWTF(uint32_t width, uint32_t height) { 59 | if (wtf) { 60 | WTF_Resize(wtf, width, height); 61 | } 62 | } 63 | 64 | void RButtonWTF() { 65 | if (wtf) { 66 | WTF_Pause(wtf); 67 | } 68 | } 69 | 70 | void ReleaseWTF() { 71 | if (wtf) { 72 | if (WTF_IsRunning(wtf)) { 73 | WTF_Stop(wtf); 74 | } 75 | WTF_ReleaseInstance(wtf); 76 | wtf = NULL; 77 | wtfInited = false; 78 | } 79 | } 80 | 81 | LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { 82 | switch (message) { 83 | case WM_SIZE: 84 | if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) { 85 | ResizeWTF(LOWORD(lParam), HIWORD(lParam)); 86 | } 87 | break; 88 | case WM_LBUTTONDOWN: 89 | InitializeWTF(hwnd); 90 | break; 91 | case WM_RBUTTONDOWN: 92 | RButtonWTF(); 93 | break; 94 | case WM_DESTROY: 95 | ReleaseWTF(); 96 | break; 97 | case WM_DPICHANGED: { 98 | uint32_t dpi = LOWORD(wParam); 99 | if (wtf != NULL && WTF_IsRunning(wtf)) { 100 | WTF_SetDpi(wtf, dpi, dpi); 101 | } 102 | break; 103 | } 104 | } 105 | return WTFWindow_DefaultWindowProc(window, (void*)hwnd, message, (WPARAM)wParam, (LPARAM)lParam); 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libwtfdanmaku 2 | ============= 3 | High-performance danmaku engine for Windows, based on Direct2D and DirectComposition API. 4 | 5 | This project is still under developing. 6 | 7 | ### Features 8 | - Hardware accelerated by Direct2D, DirectWrite, Direct3D 11.0 and DXGI 1.2 9 | - Supports Windows 8 or later, Windows 8.1 Store App, and Windows 7 with [Platform Update](https://msdn.microsoft.com/en-us/library/windows/desktop/jj863687(v=vs.85).aspx) 10 | - Few cpu usage (<= 5%) with smooth animation 11 | - Accurate danmaku layout 12 | 13 | ### TODO 14 | - ~~Renderable's bitmap invalid issue~~ 15 | - ~~Multiple instance~~ 16 | - ~~x64 compatible~~ 17 | - Optimize danmaku style effect 18 | - Rendering Statistics 19 | - ~~Hi-DPI support, and Pre-monitor DPI Aware~~ 20 | - Further performance optimization 21 | - ~~Resize support (need rebuild device-dependent resources)~~ 22 | - ~~BottomDanmaku~~, L2RDanmaku, Mode7 support, etc. 23 | - Refined error handling 24 | - User-friendly ~~C & C++ API~~ 25 | - C++/CLI wrapper for .NET 26 | - Interoperate with Media Player 27 | - ~~Optimize Renderable building~~ 28 | - ~~Windows Phone / UWP compatible~~ 29 | - ~~Windows 7 compatible (maybe LayeredWindow?)~~ 30 | - ~~Move to Direct3D 11~~ 31 | - R2LDanmaku lines limitation 32 | - Keywords filter by regular expression 33 | - C++/CX wrapper for C#/WinRT invoking 34 | - More smooth animation (percision problem when calculating coordinates?) 35 | - ~~Refactor project files to vcxitems (Shared/Windows/WindowsPhone)~~ 36 | 37 | ### Build 38 | My environment: Visual Studio Community 2013. Also tested under VS2015. 39 | 40 | Don't forget to checkout submodule (git submodule update --init) 41 | 42 | - Win32 Desktop 43 | - Import project `libwtfdanmaku.Desktop` 44 | - Win32 Desktop (for Windows7 compatibility) 45 | - Import project `libwtfdanmaku.Windows7` 46 | - Windows 10 Universal Platform 47 | - Import project `libwtfdanmaku.Universal` 48 | - Windows 8.1 WinRT/Universal 49 | - Import project `libwtfdanmaku.Windows` 50 | - Windows Phone 8.1 51 | - Import project `libwtfdanmaku.WindowsPhone` 52 | 53 | libwtfdanmaku is permitted to work under shared library (DLL) according to the license. 54 | 55 | ### Screenshot 56 | 57 | ![5cmps](https://raw.githubusercontent.com/xqq/xqq.github.io/master/img/wtf_screenshot_1.jpg) 58 | 59 | ### License 60 | Copyright (C) 2015 xqq 61 | 62 | Licensed under LGPL v2.1 -------------------------------------------------------------------------------- /WTFDanmaku.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libwtfdanmaku", "libwtfdanmaku", "{E11B4418-B776-440A-93EB-E8C781728135}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.Windows", "libwtfdanmaku.Windows\libwtfdanmaku.Windows.vcxproj", "{BD09F5C8-43DC-4034-AD9C-5C8F7097073E}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.Shared", "src\libwtfdanmaku.Shared.vcxitems", "{6B39FFA8-CD29-4E39-8BB2-8C6B1F6EF2F9}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.WindowsPhone", "libwtfdanmaku.WindowsPhone\libwtfdanmaku.WindowsPhone.vcxproj", "{4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.Universal", "libwtfdanmaku.Universal\libwtfdanmaku.Universal.vcxproj", "{51D09FCD-92E8-4200-9138-4B06A1FF63A7}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.Desktop", "libwtfdanmaku.Desktop\libwtfdanmaku.Desktop.vcxproj", "{817EBE08-2BE4-4251-92D0-117822D8F16A}" 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwtfdanmaku.Windows7", "libwtfdanmaku.Windows7\libwtfdanmaku.Windows7.vcxproj", "{EB79010D-CCFC-4A09-93D9-E3DBFC61092F}" 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo.Desktop", "Demo.Desktop\Demo.Desktop.vcxproj", "{6C6C2A8C-E868-4343-BD6E-6D164BF25134}" 21 | EndProject 22 | Global 23 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 24 | src\libwtfdanmaku.Shared.vcxitems*{51d09fcd-92e8-4200-9138-4b06a1ff63a7}*SharedItemsImports = 4 25 | src\libwtfdanmaku.Shared.vcxitems*{bd09f5c8-43dc-4034-ad9c-5c8f7097073e}*SharedItemsImports = 4 26 | src\libwtfdanmaku.Shared.vcxitems*{817ebe08-2be4-4251-92d0-117822d8f16a}*SharedItemsImports = 4 27 | src\libwtfdanmaku.Shared.vcxitems*{eb79010d-ccfc-4a09-93d9-e3dbfc61092f}*SharedItemsImports = 4 28 | src\libwtfdanmaku.Shared.vcxitems*{6b39ffa8-cd29-4e39-8bb2-8c6b1f6ef2f9}*SharedItemsImports = 9 29 | src\libwtfdanmaku.Shared.vcxitems*{4f9b663c-244b-4dd6-9f1c-32be2bcbd045}*SharedItemsImports = 4 30 | EndGlobalSection 31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 32 | Debug|ARM = Debug|ARM 33 | Debug|Win32 = Debug|Win32 34 | Debug|x64 = Debug|x64 35 | Release|ARM = Release|ARM 36 | Release|Win32 = Release|Win32 37 | Release|x64 = Release|x64 38 | EndGlobalSection 39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 40 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|ARM.ActiveCfg = Debug|ARM 41 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|ARM.Build.0 = Debug|ARM 42 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|Win32.ActiveCfg = Debug|Win32 43 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|Win32.Build.0 = Debug|Win32 44 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|x64.ActiveCfg = Debug|x64 45 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Debug|x64.Build.0 = Debug|x64 46 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|ARM.ActiveCfg = Release|ARM 47 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|ARM.Build.0 = Release|ARM 48 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|Win32.ActiveCfg = Release|Win32 49 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|Win32.Build.0 = Release|Win32 50 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|x64.ActiveCfg = Release|x64 51 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E}.Release|x64.Build.0 = Release|x64 52 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Debug|ARM.ActiveCfg = Debug|ARM 53 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Debug|ARM.Build.0 = Debug|ARM 54 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Debug|Win32.ActiveCfg = Debug|Win32 55 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Debug|Win32.Build.0 = Debug|Win32 56 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Debug|x64.ActiveCfg = Debug|Win32 57 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Release|ARM.ActiveCfg = Release|ARM 58 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Release|ARM.Build.0 = Release|ARM 59 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Release|Win32.ActiveCfg = Release|Win32 60 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Release|Win32.Build.0 = Release|Win32 61 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045}.Release|x64.ActiveCfg = Release|Win32 62 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|ARM.ActiveCfg = Debug|ARM 63 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|ARM.Build.0 = Debug|ARM 64 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|Win32.ActiveCfg = Debug|Win32 65 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|Win32.Build.0 = Debug|Win32 66 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|x64.ActiveCfg = Debug|x64 67 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Debug|x64.Build.0 = Debug|x64 68 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|ARM.ActiveCfg = Release|ARM 69 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|ARM.Build.0 = Release|ARM 70 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|Win32.ActiveCfg = Release|Win32 71 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|Win32.Build.0 = Release|Win32 72 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|x64.ActiveCfg = Release|x64 73 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7}.Release|x64.Build.0 = Release|x64 74 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Debug|ARM.ActiveCfg = Debug|Win32 75 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Debug|Win32.ActiveCfg = Debug|Win32 76 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Debug|Win32.Build.0 = Debug|Win32 77 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Debug|x64.ActiveCfg = Debug|x64 78 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Debug|x64.Build.0 = Debug|x64 79 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Release|ARM.ActiveCfg = Release|Win32 80 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Release|Win32.ActiveCfg = Release|Win32 81 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Release|Win32.Build.0 = Release|Win32 82 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Release|x64.ActiveCfg = Release|x64 83 | {817EBE08-2BE4-4251-92D0-117822D8F16A}.Release|x64.Build.0 = Release|x64 84 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Debug|ARM.ActiveCfg = Debug|Win32 85 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Debug|Win32.ActiveCfg = Debug|Win32 86 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Debug|Win32.Build.0 = Debug|Win32 87 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Debug|x64.ActiveCfg = Debug|x64 88 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Debug|x64.Build.0 = Debug|x64 89 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Release|ARM.ActiveCfg = Release|Win32 90 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Release|Win32.ActiveCfg = Release|Win32 91 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Release|Win32.Build.0 = Release|Win32 92 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Release|x64.ActiveCfg = Release|x64 93 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F}.Release|x64.Build.0 = Release|x64 94 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Debug|ARM.ActiveCfg = Debug|Win32 95 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Debug|Win32.ActiveCfg = Debug|Win32 96 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Debug|Win32.Build.0 = Debug|Win32 97 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Debug|x64.ActiveCfg = Debug|x64 98 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Debug|x64.Build.0 = Debug|x64 99 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Release|ARM.ActiveCfg = Release|Win32 100 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Release|Win32.ActiveCfg = Release|Win32 101 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Release|Win32.Build.0 = Release|Win32 102 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Release|x64.ActiveCfg = Release|x64 103 | {6C6C2A8C-E868-4343-BD6E-6D164BF25134}.Release|x64.Build.0 = Release|x64 104 | EndGlobalSection 105 | GlobalSection(SolutionProperties) = preSolution 106 | HideSolutionNode = FALSE 107 | EndGlobalSection 108 | GlobalSection(NestedProjects) = preSolution 109 | {BD09F5C8-43DC-4034-AD9C-5C8F7097073E} = {E11B4418-B776-440A-93EB-E8C781728135} 110 | {6B39FFA8-CD29-4E39-8BB2-8C6B1F6EF2F9} = {E11B4418-B776-440A-93EB-E8C781728135} 111 | {4F9B663C-244B-4DD6-9F1C-32BE2BCBD045} = {E11B4418-B776-440A-93EB-E8C781728135} 112 | {51D09FCD-92E8-4200-9138-4B06A1FF63A7} = {E11B4418-B776-440A-93EB-E8C781728135} 113 | {817EBE08-2BE4-4251-92D0-117822D8F16A} = {E11B4418-B776-440A-93EB-E8C781728135} 114 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F} = {E11B4418-B776-440A-93EB-E8C781728135} 115 | EndGlobalSection 116 | EndGlobal 117 | -------------------------------------------------------------------------------- /include/WTFDanmaku.h: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DANMAKU_C_WRAPPER_H 2 | #define _WTF_DANMAKU_C_WRAPPER_H 3 | 4 | #include 5 | #include 6 | #include "WTF_API.h" 7 | 8 | WTF_C_API typedef struct WTF_Instance { 9 | void* controller; 10 | } WTF_Instance; 11 | 12 | #define WTF_DANMAKU_TYPE_SCROLLING 1 13 | #define WTF_DANMAKU_TYPE_BOTTOM 4 14 | #define WTF_DANMAKU_TYPE_TOP 5 15 | #define WTF_DANMAKU_TYPE_RESERVE 6 16 | #define WTF_DANMAKU_TYPE_POSITION 7 17 | #define WTF_DANMAKU_TYPE_ADVANCED 8 18 | 19 | #define WTF_DANMAKU_STYLE_OUTLINE 1 20 | #define WTF_DANMAKU_STYLE_PROJECTION 2 21 | 22 | #define WTF_DANMAKU_TYPE_SCROLLING_VISIBLE (1) 23 | #define WTF_DANMAKU_TYPE_BOTTOM_VISIBLE (2) 24 | #define WTF_DANMAKU_TYPE_TOP_VISIBLE (4) 25 | #define WTF_DANMAKU_TYPE_RESERVE_VISIBLE (8) 26 | #define WTF_DANMAKU_TYPE_POSITION_VISIBLE (16) 27 | #define WTF_DANMAKU_TYPE_ADVANCED_VISIBLE (32) 28 | 29 | WTF_C_API WTF_Instance* __stdcall WTF_CreateInstance(); 30 | WTF_C_API void __stdcall WTF_ReleaseInstance(WTF_Instance* instance); 31 | WTF_C_API int __stdcall WTF_InitializeWithHwnd(WTF_Instance* instance, void* hwnd); 32 | WTF_C_API int __stdcall WTF_InitializeOffscreen(WTF_Instance* instance, uint32_t initialWidth, uint32_t initialHeight); 33 | WTF_C_API void __stdcall WTF_Terminate(WTF_Instance* instance); 34 | WTF_C_API int __stdcall WTF_QuerySwapChain(WTF_Instance* instance, const void* pGuid, void** ppObject); 35 | WTF_C_API void __stdcall WTF_LoadBilibiliFile(WTF_Instance* instance, const char* filePath); 36 | WTF_C_API void __stdcall WTF_LoadBilibiliFileW(WTF_Instance* instance, const wchar_t* filePath); 37 | WTF_C_API void __stdcall WTF_LoadBilibiliXml(WTF_Instance* instance, const char* str); 38 | WTF_C_API void __stdcall WTF_AddDanmaku(WTF_Instance* instance, int type, int64_t time, const wchar_t* comment, int fontSize, int fontColor, int64_t timestamp, int danmakuId); 39 | WTF_C_API void __stdcall WTF_AddLiveDanmaku(WTF_Instance* instance, int type, int64_t time, const wchar_t* comment, int fontSize, int fontColor, int64_t timestamp, int danmakuId); 40 | WTF_C_API void __stdcall WTF_Start(WTF_Instance* instance); 41 | WTF_C_API void __stdcall WTF_Pause(WTF_Instance* instance); 42 | WTF_C_API void __stdcall WTF_Resume(WTF_Instance* instance); 43 | WTF_C_API void __stdcall WTF_Stop(WTF_Instance* instance); 44 | WTF_C_API void __stdcall WTF_SeekTo(WTF_Instance* instance, int64_t milliseconds); 45 | WTF_C_API void __stdcall WTF_Resize(WTF_Instance* instance, uint32_t width, uint32_t height); 46 | WTF_C_API void __stdcall WTF_SetDpi(WTF_Instance* instance, uint32_t dpiX, uint32_t dpiY); 47 | WTF_C_API int64_t __stdcall WTF_GetCurrentPosition(WTF_Instance* instance); 48 | WTF_C_API int __stdcall WTF_IsRunning(WTF_Instance* instance); 49 | WTF_C_API float __stdcall WTF_GetFontScaleFactor(WTF_Instance* instance); 50 | WTF_C_API void __stdcall WTF_SetFontScaleFactor(WTF_Instance* instance, float factor); 51 | WTF_C_API void __stdcall WTF_SetFontName(WTF_Instance* instance, const wchar_t* fontName); 52 | WTF_C_API void __stdcall WTF_SetFontWeight(WTF_Instance* instance, int dwriteFontWeight); 53 | WTF_C_API void __stdcall WTF_SetFontStyle(WTF_Instance* instance, int dwriteFontStyle); 54 | WTF_C_API void __stdcall WTF_SetFontStretch(WTF_Instance* instance, int dwriteFontStretch); 55 | WTF_C_API void __stdcall WTF_SetDanmakuStyle(WTF_Instance* instance, int style); 56 | WTF_C_API void __stdcall WTF_SetCompositionOpacity(WTF_Instance* instance, float opacity); 57 | WTF_C_API void __stdcall WTF_SetDanmakuTypeVisibility(WTF_Instance* instance, int params); 58 | 59 | #ifndef _WTF_BUILD_UWP 60 | 61 | WTF_C_API typedef struct WTF_Window WTF_Window; 62 | 63 | WTF_C_API WTF_Window* __stdcall WTFWindow_Create(void* hInstance, int nCmdShow); 64 | WTF_C_API void __stdcall WTFWindow_Release(WTF_Window* window); 65 | WTF_C_API void __stdcall WTFWindow_Initialize(WTF_Window* window, uint32_t dwExStyle, int width, int height, const wchar_t* title); 66 | WTF_C_API void* __stdcall WTFWindow_GetHwnd(WTF_Window* window); 67 | WTF_C_API void __stdcall WTFWindow_SetCustomWndProc(WTF_Window* window, void* proc); 68 | WTF_C_API LRESULT __stdcall WTFWindow_DefaultWindowProc(WTF_Window* window, void* hwnd, uint32_t message, WPARAM wParam, LPARAM lParam); 69 | WTF_C_API void __stdcall WTFWindow_SetHitTestOverEnabled(WTF_Window* window, int enabled); 70 | WTF_C_API int __stdcall WTFWindow_RunMessageLoop(WTF_Window* window); 71 | 72 | #endif // !_WTF_BUILD_UWP 73 | 74 | 75 | #endif // _WTF_DANMAKU_C_WRAPPER_H -------------------------------------------------------------------------------- /include/WTFEngine.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_ENGINE_HPP 2 | #define _WTF_ENGINE_HPP 3 | 4 | #include 5 | #include "WTF_API.h" 6 | 7 | namespace WTFDanmaku { 8 | 9 | class Controller; 10 | 11 | enum Type : int { 12 | Scrolling = 1, 13 | Bottom = 4, 14 | Top = 5, 15 | Reverse = 6, 16 | Position = 7, 17 | Advanced = 8 18 | }; 19 | 20 | enum TypeVisible : int { 21 | ScrollingVisible = 1, 22 | BottomVisible = 2, 23 | TopVisible = 4, 24 | ReserveVisible = 8, 25 | PositionVisible = 16, 26 | AdvancedVisible = 32 27 | }; 28 | 29 | enum Style : int { 30 | Outline = 1, 31 | Projection = 2 32 | }; 33 | 34 | class WTF_API WTFEngine { 35 | public: 36 | explicit WTFEngine(); 37 | ~WTFEngine(); 38 | int Initialize(void* hwnd); 39 | int InitializeOffscreen(uint32_t initialWidth, uint32_t initialHeight); 40 | void Terminate(); 41 | int QuerySwapChain(const void* pGuid, void** ppObject); 42 | void LoadBilibiliFile(const char* filePath); 43 | void LoadBilibiliFile(const wchar_t* filePath); 44 | void LoadBilibiliXml(const char* str); 45 | void AddDanmaku(Type type, time_t time, const wchar_t* comment, int fontSize, int fontColor, time_t timestamp = 0, int danmakuId = 0); 46 | void AddLiveDanmaku(Type type, time_t time, const wchar_t* comment, int fontSize, int fontColor, time_t timestamp = 0, int danmakuId = 0); 47 | void Start(); 48 | void Pause(); 49 | void Resume(); 50 | void Stop(); 51 | void SeekTo(time_t milliseconds); 52 | void Resize(uint32_t width, uint32_t height); 53 | void SetDpi(uint32_t dpiX, uint32_t dpiY); 54 | time_t GetCurrentPosition(); 55 | bool IsRunning(); 56 | float GetFontScaleFactor(); 57 | void SetFontScaleFactor(float factor); 58 | void SetFontName(const wchar_t* fontName); 59 | void SetFontWeight(int dwriteFontWeight); 60 | void SetFontStyle(int dwriteFontStyle); 61 | void SetFontStretch(int dwriteFontStretch); 62 | void SetDanmakuStyle(Style style); 63 | void SetCompositionOpacity(float opacity); 64 | void SetDanmakuTypeVisibility(int params); 65 | private: 66 | WTFEngine(const WTFEngine&) = delete; 67 | WTFEngine& operator=(const WTFEngine&) = delete; 68 | private: 69 | Controller* mController; 70 | }; 71 | 72 | } 73 | 74 | 75 | #endif // _WTF_ENGINE_HPP -------------------------------------------------------------------------------- /include/WTFWindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_WINDOW_HPP 2 | #define _WTF_WINDOW_HPP 3 | 4 | #include 5 | #include 6 | #include "WTF_API.h" 7 | 8 | #ifndef _WTF_BUILD_UWP 9 | 10 | namespace WTFDanmaku { 11 | 12 | class WTF_API WTFWindow { 13 | public: 14 | explicit WTFWindow(HINSTANCE hInst, int nCmdShow); 15 | ~WTFWindow(); 16 | int Initialize(DWORD dwExStyle = NULL, int width = 1280, int height = 720, const wchar_t* title = nullptr); 17 | void SetCustomWindowProc(WNDPROC proc); 18 | void SetHitTestOverEnabled(bool enabled); 19 | int Run(); 20 | HWND GetHwnd(); 21 | LRESULT DefaultWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 22 | private: 23 | ATOM RegisterWindowClass(); 24 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 25 | private: 26 | WTFWindow(const WTFWindow&) = delete; 27 | WTFWindow& operator=(const WTFWindow&) = delete; 28 | private: 29 | int m_nCmdShow = 0; 30 | HINSTANCE m_hInstance = NULL; 31 | WNDPROC m_CustomWndProc = NULL; 32 | HWND m_hWindow = NULL; 33 | PWSTR m_WindowClassName = L"WTFDanmaku"; 34 | 35 | uint32_t m_ClientWidth = 0; 36 | uint32_t m_ClientHeight = 0; 37 | }; 38 | 39 | } 40 | 41 | #endif // !_WTF_BUILD_UWP 42 | 43 | #endif // _WTF_SAMPLE_WINDOW_HPP -------------------------------------------------------------------------------- /include/WTF_API.h: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_API_H 2 | #define _WTF_API_H 3 | 4 | #ifdef _MSC_VER 5 | #ifdef LIBWTFDANMAKU_EXPORTS 6 | #define WTF_API __declspec(dllexport) 7 | #else 8 | #define WTF_API __declspec(dllimport) 9 | #endif 10 | #else 11 | #define WTF_API 12 | #endif 13 | 14 | #ifdef __cplusplus 15 | #define WTF_C_API extern "C" WTF_API 16 | #else 17 | #define WTF_C_API WTF_API 18 | #endif 19 | 20 | #endif // _WTF_API_H -------------------------------------------------------------------------------- /libwtfdanmaku.Desktop/libwtfdanmaku.Desktop.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {817EBE08-2BE4-4251-92D0-117822D8F16A} 23 | Win32Proj 24 | libwtfdanmaku 25 | libwtfdanmaku.Desktop 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v120 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | true 37 | v120 38 | Unicode 39 | 40 | 41 | DynamicLibrary 42 | false 43 | v120 44 | true 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v120 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(ItemsRootNamespace) 76 | $(SolutionDir)$(Configuration)\$(MSBuildProjectName)\ 77 | 78 | 79 | true 80 | $(ItemsRootNamespace) 81 | $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ 82 | 83 | 84 | false 85 | $(ItemsRootNamespace) 86 | $(SolutionDir)$(Configuration)\$(MSBuildProjectName)\ 87 | 88 | 89 | false 90 | $(ItemsRootNamespace) 91 | $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ 92 | 93 | 94 | 95 | Level3 96 | Disabled 97 | WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;%(PreprocessorDefinitions) 98 | true 99 | true 100 | false 101 | 102 | 103 | Windows 104 | true 105 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 106 | 107 | 108 | 109 | 110 | Level3 111 | Disabled 112 | WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;%(PreprocessorDefinitions) 113 | true 114 | true 115 | false 116 | 117 | 118 | Windows 119 | true 120 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 121 | 122 | 123 | 124 | 125 | Level3 126 | MaxSpeed 127 | true 128 | true 129 | WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;%(PreprocessorDefinitions) 130 | true 131 | true 132 | 133 | 134 | Windows 135 | true 136 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;%(PreprocessorDefinitions) 148 | true 149 | true 150 | 151 | 152 | Windows 153 | true 154 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 155 | true 156 | true 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /libwtfdanmaku.Desktop/libwtfdanmaku.Desktop.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | -------------------------------------------------------------------------------- /libwtfdanmaku.Universal/libwtfdanmaku.Universal.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | ARM 19 | 20 | 21 | Release 22 | Win32 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | {51d09fcd-92e8-4200-9138-4b06a1ff63a7} 31 | DynamicLibrary 32 | libwtfdanmaku.Universal 33 | libwtfdanmaku 34 | zh-CN 35 | 14.0 36 | true 37 | Windows Store 38 | 10.0.10586.0 39 | 10.0.10240.0 40 | 10.0 41 | 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v140 47 | 48 | 49 | DynamicLibrary 50 | true 51 | v140 52 | 53 | 54 | DynamicLibrary 55 | true 56 | v140 57 | 58 | 59 | DynamicLibrary 60 | false 61 | true 62 | v140 63 | 64 | 65 | DynamicLibrary 66 | false 67 | true 68 | v140 69 | 70 | 71 | DynamicLibrary 72 | false 73 | true 74 | v140 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | false 104 | false 105 | $(ItemsRootNamespace) 106 | 107 | 108 | false 109 | false 110 | $(ItemsRootNamespace) 111 | 112 | 113 | false 114 | false 115 | $(ItemsRootNamespace) 116 | 117 | 118 | false 119 | false 120 | $(ItemsRootNamespace) 121 | 122 | 123 | false 124 | false 125 | $(ItemsRootNamespace) 126 | 127 | 128 | false 129 | false 130 | $(ItemsRootNamespace) 131 | 132 | 133 | 134 | NotUsing 135 | false 136 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 137 | 138 | 139 | Console 140 | false 141 | false 142 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 143 | 144 | 145 | 146 | 147 | NotUsing 148 | false 149 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 150 | 151 | 152 | Console 153 | false 154 | false 155 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 156 | 157 | 158 | 159 | 160 | NotUsing 161 | false 162 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 163 | 164 | 165 | Console 166 | false 167 | false 168 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 169 | 170 | 171 | 172 | 173 | NotUsing 174 | false 175 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 176 | 177 | 178 | Console 179 | false 180 | false 181 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 182 | 183 | 184 | 185 | 186 | NotUsing 187 | false 188 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 189 | 190 | 191 | Console 192 | false 193 | false 194 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 195 | 196 | 197 | 198 | 199 | NotUsing 200 | false 201 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 202 | 203 | 204 | Console 205 | false 206 | false 207 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /libwtfdanmaku.Universal/libwtfdanmaku.Universal.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | -------------------------------------------------------------------------------- /libwtfdanmaku.Windows/libwtfdanmaku.Windows.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | ARM 19 | 20 | 21 | Release 22 | Win32 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | {bd09f5c8-43dc-4034-ad9c-5c8f7097073e} 31 | DynamicLibrary 32 | libwtfdanmaku 33 | zh-CN 34 | 12.0 35 | true 36 | Windows Store 37 | 8.1 38 | CodeSharingDll 39 | 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v120 45 | 46 | 47 | DynamicLibrary 48 | true 49 | v120 50 | 51 | 52 | DynamicLibrary 53 | true 54 | v120 55 | 56 | 57 | DynamicLibrary 58 | false 59 | true 60 | v120 61 | 62 | 63 | DynamicLibrary 64 | false 65 | true 66 | v120 67 | 68 | 69 | DynamicLibrary 70 | false 71 | true 72 | v120 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | false 101 | false 102 | $(ItemsRootNamespace) 103 | 104 | 105 | false 106 | false 107 | $(ItemsRootNamespace) 108 | 109 | 110 | false 111 | false 112 | $(ItemsRootNamespace) 113 | 114 | 115 | false 116 | false 117 | $(ItemsRootNamespace) 118 | 119 | 120 | false 121 | false 122 | $(ItemsRootNamespace) 123 | 124 | 125 | false 126 | false 127 | $(ItemsRootNamespace) 128 | 129 | 130 | 131 | NotUsing 132 | false 133 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 134 | 135 | 136 | Console 137 | false 138 | false 139 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 140 | 141 | 142 | 143 | 144 | NotUsing 145 | false 146 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 147 | 148 | 149 | Console 150 | false 151 | false 152 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 153 | 154 | 155 | 156 | 157 | NotUsing 158 | false 159 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 160 | 161 | 162 | Console 163 | false 164 | false 165 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 166 | 167 | 168 | 169 | 170 | NotUsing 171 | false 172 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 173 | 174 | 175 | Console 176 | false 177 | false 178 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 179 | 180 | 181 | 182 | 183 | NotUsing 184 | false 185 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 186 | 187 | 188 | Console 189 | false 190 | false 191 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 192 | 193 | 194 | 195 | 196 | NotUsing 197 | false 198 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 199 | 200 | 201 | Console 202 | false 203 | false 204 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /libwtfdanmaku.Windows/libwtfdanmaku.Windows.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /libwtfdanmaku.Windows7/libwtfdanmaku.Windows7.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EB79010D-CCFC-4A09-93D9-E3DBFC61092F} 23 | Win32Proj 24 | libwtfdanmaku 25 | libwtfdanmaku.Windows7 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v120 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | true 37 | v120 38 | Unicode 39 | 40 | 41 | DynamicLibrary 42 | false 43 | v120 44 | true 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v120 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(ItemsRootNamespace) 76 | $(SolutionDir)$(Configuration)\$(MSBuildProjectName)\ 77 | 78 | 79 | true 80 | $(ItemsRootNamespace) 81 | $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ 82 | 83 | 84 | false 85 | $(ItemsRootNamespace) 86 | $(SolutionDir)$(Configuration)\$(MSBuildProjectName)\ 87 | 88 | 89 | false 90 | $(ItemsRootNamespace) 91 | $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ 92 | 93 | 94 | 95 | Level3 96 | Disabled 97 | WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_WIN7;%(PreprocessorDefinitions) 98 | true 99 | true 100 | false 101 | 102 | 103 | Windows 104 | true 105 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 106 | 107 | 108 | 109 | 110 | Level3 111 | Disabled 112 | WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_WIN7;%(PreprocessorDefinitions) 113 | true 114 | true 115 | false 116 | 117 | 118 | Windows 119 | true 120 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 121 | 122 | 123 | 124 | 125 | Level3 126 | MaxSpeed 127 | true 128 | true 129 | WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_WIN7;%(PreprocessorDefinitions) 130 | true 131 | true 132 | 133 | 134 | Windows 135 | true 136 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_WIN7;%(PreprocessorDefinitions) 148 | true 149 | true 150 | 151 | 152 | Windows 153 | true 154 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 155 | true 156 | true 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /libwtfdanmaku.Windows7/libwtfdanmaku.Windows7.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | -------------------------------------------------------------------------------- /libwtfdanmaku.WindowsPhone/libwtfdanmaku.WindowsPhone.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Release 14 | ARM 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | 22 | {4f9b663c-244b-4dd6-9f1c-32be2bcbd045} 23 | DynamicLibrary 24 | libwtfdanmaku 25 | zh-CN 26 | 12.0 27 | true 28 | Windows Phone 29 | 8.1 30 | CodeSharingDll 31 | 32 | 33 | 34 | DynamicLibrary 35 | true 36 | v120_wp81 37 | 38 | 39 | DynamicLibrary 40 | true 41 | v120_wp81 42 | 43 | 44 | DynamicLibrary 45 | false 46 | true 47 | v120_wp81 48 | 49 | 50 | DynamicLibrary 51 | false 52 | true 53 | v120_wp81 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | false 76 | false 77 | $(ItemsRootNamespace) 78 | 79 | 80 | false 81 | false 82 | $(ItemsRootNamespace) 83 | 84 | 85 | false 86 | false 87 | $(ItemsRootNamespace) 88 | 89 | 90 | false 91 | false 92 | $(ItemsRootNamespace) 93 | 94 | 95 | 96 | NotUsing 97 | false 98 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 99 | 100 | 101 | Console 102 | false 103 | false 104 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 105 | 106 | 107 | 108 | 109 | NotUsing 110 | false 111 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 112 | 113 | 114 | Console 115 | false 116 | false 117 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 118 | 119 | 120 | 121 | 122 | NotUsing 123 | false 124 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 125 | 126 | 127 | Console 128 | false 129 | false 130 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 131 | 132 | 133 | 134 | 135 | NotUsing 136 | false 137 | WIN32;_WINDOWS;_USRDLL;LIBWTFDANMAKU_EXPORTS;_WTF_BUILD_UWP;%(PreprocessorDefinitions) 138 | 139 | 140 | Console 141 | false 142 | false 143 | dxgi.lib;d3d11.lib;d2d1.lib;dwrite.lib;%(AdditionalDependencies) 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /libwtfdanmaku.WindowsPhone/libwtfdanmaku.WindowsPhone.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/BaseDanmaku.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "DanmakuConfig.hpp" 3 | #include "Renderable.hpp" 4 | #include "BaseDanmaku.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | BaseDanmaku::BaseDanmaku() { 9 | 10 | } 11 | 12 | BaseDanmaku::~BaseDanmaku() { 13 | 14 | } 15 | 16 | DanmakuType BaseDanmaku::GetType() { 17 | return kNull; 18 | } 19 | 20 | bool BaseDanmaku::HasMeasured(DanmakuConfig* config) { 21 | return nullptr != mRenderable 22 | && mRenderable->HasTextLayout() 23 | && mMeasureFlag == config->MeasureFlag; 24 | } 25 | 26 | bool BaseDanmaku::HasLayout(DanmakuConfig* config) { 27 | return mLayoutFlag == config->LayoutFlag; 28 | } 29 | 30 | void BaseDanmaku::Measure(Displayer* displayer, DanmakuConfig* config) { 31 | if (nullptr == mRenderable) { 32 | mRenderable = std::make_shared(this); 33 | } 34 | 35 | mRenderable->BuildTextLayout(displayer, config); 36 | 37 | mDuration = config->DanmakuDuration; 38 | mMeasureFlag = config->MeasureFlag; 39 | } 40 | 41 | void BaseDanmaku::ReleaseResources() { 42 | this->ReleaseRenderable(); 43 | } 44 | 45 | bool BaseDanmaku::IsAlive(time_t time) { 46 | time_t elapsed = time - this->mStartTime; 47 | return elapsed > 0 && elapsed <= mDuration; 48 | } 49 | 50 | weak_ptr BaseDanmaku::BuildRenderable(Displayer* displayer, DanmakuConfig* config) { 51 | if (nullptr == mRenderable || !mRenderable->HasTextLayout()) 52 | this->Measure(displayer, config); 53 | 54 | if (!mRenderable->HasBitmap(config)) { 55 | mRenderable->BuildBitmap(displayer, config); 56 | } 57 | 58 | return mRenderable; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/BaseDanmaku.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_BASE_DANMAKU_HPP 2 | #define _WTF_BASE_DANMAKU_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "base/RefBase.hpp" 8 | #include "base/RefPtr.hpp" 9 | #include "Noncopyable.hpp" 10 | #include "Rect.hpp" 11 | 12 | using std::shared_ptr; 13 | using std::weak_ptr; 14 | 15 | namespace WTFDanmaku { 16 | 17 | enum DanmakuType : int { 18 | kNull = 0, 19 | kScrolling = 1, 20 | kBottom = 4, 21 | kTop = 5, 22 | kReverse = 6, 23 | kPosition = 7, 24 | kAdvanced = 8 25 | }; 26 | 27 | class Displayer; 28 | class Renderable; 29 | struct DanmakuConfig; 30 | 31 | class BaseDanmaku : public xl::RefBase { 32 | friend class DanmakuFactory; 33 | friend class Renderable; 34 | public: 35 | explicit BaseDanmaku(); 36 | 37 | virtual ~BaseDanmaku(); 38 | 39 | inline float GetWidth() { 40 | return mTextWidth; 41 | } 42 | 43 | inline float GetHeight() { 44 | return mTextHeight; 45 | } 46 | 47 | bool HasMeasured(DanmakuConfig* config); 48 | 49 | bool HasLayout(DanmakuConfig* config); 50 | 51 | inline bool HasRenderable() { 52 | return mRenderable != nullptr; 53 | } 54 | 55 | weak_ptr BuildRenderable(Displayer* displayer, DanmakuConfig* config); 56 | 57 | inline weak_ptr GetRenderable() { 58 | return weak_ptr(mRenderable); 59 | } 60 | 61 | inline void ReleaseRenderable() { 62 | mRenderable.reset(); 63 | } 64 | 65 | virtual void ReleaseResources(); 66 | 67 | virtual void Measure(Displayer* displayer, DanmakuConfig* config); 68 | 69 | virtual void Layout(Displayer* displayer, DanmakuConfig* config, float x, float y) = 0; 70 | 71 | virtual DanmakuType GetType() = 0; 72 | 73 | virtual bool IsAlive(time_t time); 74 | 75 | virtual float GetSpeed() = 0; 76 | 77 | virtual float GetLeftAtTime(Displayer* displayer, time_t time) = 0; 78 | 79 | virtual Rect GetRectAtTime(Displayer* displayer, time_t time) = 0; 80 | 81 | inline time_t GetStartTime() { 82 | return mStartTime; 83 | } 84 | 85 | inline void SetStartTime(time_t time) { 86 | mStartTime = time; 87 | } 88 | 89 | inline Rect GetRect() { 90 | return mRect; 91 | } 92 | 93 | inline time_t GetSendTimestamp() { 94 | return mTimestamp; 95 | } 96 | 97 | inline uint32_t GetDanmakuId() { 98 | return mDanmakuId; 99 | } 100 | 101 | inline bool IsSkipped() { 102 | return mSkipped; 103 | } 104 | 105 | inline void SetSkipped(bool skipped) { 106 | mSkipped = skipped; 107 | } 108 | protected: 109 | time_t mStartTime = 0; 110 | time_t mDuration = 0; 111 | Rect mRect; 112 | float mTextWidth = -1.0f; 113 | float mTextHeight = -1.0f; 114 | int mLayoutFlag = -1; 115 | private: 116 | std::wstring mComment; 117 | float mTextSize = 0.0f; 118 | uint32_t mTextColor = 0; 119 | uint32_t mTextShadowColor = 0; 120 | uint32_t mFilterParams = 0; 121 | time_t mTimestamp = 0; 122 | uint32_t mDanmakuId = 0; 123 | int mMeasureFlag = 0; 124 | shared_ptr mRenderable; 125 | bool mSkipped = false; 126 | }; 127 | 128 | typedef xl::RefPtr DanmakuRef; 129 | 130 | } 131 | 132 | #endif // _WTF_BASE_DANMAKU_HPP -------------------------------------------------------------------------------- /src/BilibiliParser.cpp: -------------------------------------------------------------------------------- 1 | #include "BilibiliParser.hpp" 2 | #include 3 | #include 4 | #include "../3rdparty/rapidxml/rapidxml.hpp" 5 | #include "../3rdparty/rapidxml/rapidxml_utils.hpp" 6 | #include "DanmakuFactory.hpp" 7 | #include "StringUtils.hpp" 8 | 9 | using namespace rapidxml; 10 | 11 | namespace WTFDanmaku { 12 | 13 | BilibiliParser::BilibiliParser() { 14 | mDanmakus = std::move(std::unique_ptr>(new std::vector)); 15 | } 16 | 17 | bool BilibiliParser::ParseStringSource(const char* str) { 18 | return ParseXml(str); 19 | } 20 | 21 | bool BilibiliParser::ParseFileSource(const char* filePath) { 22 | file<> xmlFile(filePath); 23 | return ParseXml(xmlFile.data(), true); 24 | } 25 | 26 | bool BilibiliParser::ParseFileSource(const wchar_t* filePath) { 27 | std::ifstream file; 28 | file.open(filePath, std::ios::in || std::ios::binary || std::ios::ate); 29 | if (file.fail()) 30 | return false; 31 | 32 | file.seekg(0, std::ios::end); 33 | size_t length = static_cast(file.tellg()); 34 | 35 | std::string buffer; 36 | buffer.reserve(length + 1); 37 | buffer.resize(length); 38 | 39 | file.seekg(0, std::ios::beg); 40 | file.read(&buffer[0], length); 41 | file.close(); 42 | 43 | return ParseXml(buffer.c_str(), true); 44 | } 45 | 46 | bool BilibiliParser::ParseXml(const char* data, bool inplace) { 47 | if (mDanmakus.get() == nullptr) 48 | return false; 49 | 50 | char* buffer = nullptr; 51 | std::unique_ptr raii_buffer; 52 | 53 | if (inplace) { 54 | buffer = const_cast(data); 55 | } else { 56 | size_t buffer_size = strlen(data) + 1; 57 | buffer = new char[buffer_size]; 58 | raii_buffer = std::move(std::unique_ptr(buffer)); 59 | memset(buffer, 0, buffer_size); 60 | strcpy_s(buffer, buffer_size, data); 61 | } 62 | 63 | xml_document<> doc; 64 | doc.parse<0>(buffer); 65 | 66 | xml_node* root = doc.first_node("i"); 67 | if (root == nullptr) 68 | return false; 69 | 70 | for (auto node = root->first_node("d"); node != nullptr; node = node->next_sibling()) { 71 | const char* attr = node->first_attribute("p")->value(); 72 | std::vector attributes; 73 | attributes.reserve(8); 74 | SplitString(attr, ',', attributes); 75 | 76 | if (attributes.size() >= 8) { 77 | time_t time = static_cast(std::stod(attributes[0]) * 1000); 78 | DanmakuType type = static_cast(std::stoi(attributes[1])); 79 | int textSize = static_cast(std::stof(attributes[2])); 80 | int textColor = std::stoi(attributes[3]) | 0xFF000000; 81 | time_t timestamp = std::stoull(attributes[4]); 82 | int danmakuId = std::stoi(attributes[7]); 83 | std::wstring comment = UTF8ToWideString(node->value()); 84 | 85 | DanmakuRef danmaku = DanmakuFactory::CreateDanmaku(type, time, comment, textSize, textColor, timestamp, danmakuId); 86 | if (danmaku != nullptr) { 87 | mDanmakus->push_back(danmaku); 88 | } 89 | } 90 | } 91 | return true; 92 | } 93 | 94 | std::unique_ptr> BilibiliParser::GetDanmakus() { 95 | return std::move(mDanmakus); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/BilibiliParser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_BILIBILI_PARSER_HPP 2 | #define _WTF_BILIBILI_PARSER_HPP 3 | 4 | #include "IParser.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | class BilibiliParser : public IParser { 9 | public: 10 | static inline ParserRef Create() { 11 | return std::make_shared(); 12 | } 13 | public: 14 | explicit BilibiliParser(); 15 | virtual ~BilibiliParser() = default; 16 | virtual bool ParseStringSource(const char* str) override; 17 | virtual bool ParseFileSource(const char* filePath) override; 18 | virtual bool ParseFileSource(const wchar_t* filePath) override; 19 | virtual std::unique_ptr> GetDanmakus() override; 20 | private: 21 | bool ParseXml(const char* data, bool inplace = false); 22 | private: 23 | std::unique_ptr> mDanmakus; 24 | }; 25 | 26 | } 27 | 28 | #endif // _WTF_BILIBILI_PARSER_HPP -------------------------------------------------------------------------------- /src/BottomDanmaku.cpp: -------------------------------------------------------------------------------- 1 | #include "Displayer.hpp" 2 | #include "BottomDanmaku.hpp" 3 | #include "DanmakusRetainer.hpp" 4 | 5 | namespace WTFDanmaku { 6 | 7 | BottomDanmaku::~BottomDanmaku() { 8 | 9 | } 10 | 11 | DanmakuType BottomDanmaku::GetType() { 12 | return kBottom; 13 | } 14 | 15 | class BottomDanmaku::BottomRetainer : public IDanmakusRetainer { 16 | public: 17 | virtual ~BottomRetainer() override = default; 18 | 19 | virtual void Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis) override { 20 | if (nullptr == mDanmakus) { 21 | mDanmakus = std::make_unique(); 22 | } 23 | 24 | RemoveTimeoutDanmakus(currentMillis); 25 | 26 | if (!danmaku->IsAlive(currentMillis)) { 27 | return; 28 | } 29 | 30 | float screenBottom = static_cast(displayer->GetHeight() - 1); 31 | float bottom = screenBottom; 32 | 33 | for (auto iter = mDanmakus->begin(); iter != mDanmakus->end(); ++iter) { 34 | DanmakuRef item = iter->second; 35 | Rect itemRect = item->GetRect(); 36 | 37 | if (item.Get() == danmaku.Get()) { 38 | return; 39 | } 40 | 41 | if (itemRect.bottom < bottom) { 42 | if (danmaku->GetHeight() <= bottom - itemRect.bottom) { 43 | break; 44 | } 45 | } 46 | 47 | bottom = itemRect.top - 1.0f; 48 | if (bottom - danmaku->GetHeight() < 0.0f) { 49 | bottom = screenBottom; 50 | break; 51 | } 52 | } 53 | float top = bottom - danmaku->GetHeight(); 54 | danmaku->Layout(displayer, config, 0.0f, top); 55 | 56 | int bottomInt = static_cast(bottom); 57 | auto iter = mDanmakus->find(bottomInt); 58 | if (iter != mDanmakus->end()) { 59 | iter->second = danmaku; 60 | } else { 61 | mDanmakus->insert(std::make_pair(bottomInt, danmaku)); 62 | } 63 | 64 | } 65 | 66 | void RemoveTimeoutDanmakus(time_t time) { 67 | auto iter = mDanmakus->begin(); 68 | while (iter != mDanmakus->end()) { 69 | if (iter->second->IsAlive(time) == false) { 70 | iter = mDanmakus->erase(iter); 71 | } else { 72 | ++iter; 73 | } 74 | } 75 | } 76 | 77 | virtual void Clear() override { 78 | mDanmakus->clear(); 79 | } 80 | 81 | virtual void Release() override { 82 | mDanmakus.reset(); 83 | } 84 | private: 85 | unique_ptr mDanmakus; 86 | }; 87 | 88 | std::unique_ptr BottomDanmaku::CreateRetainer() { 89 | return std::unique_ptr(new BottomRetainer); 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/BottomDanmaku.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_BOTTOM_DANMAKU_HPP 2 | #define _WTF_BOTTOM_DANMAKU_HPP 3 | 4 | #include "TopDanmaku.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | class BottomDanmaku : public TopDanmaku { 9 | public: 10 | static inline DanmakuRef Create() { 11 | return xl::RefPtr(new BottomDanmaku); 12 | } 13 | static std::unique_ptr CreateRetainer(); 14 | public: 15 | explicit BottomDanmaku() = default; 16 | virtual ~BottomDanmaku() override; 17 | virtual DanmakuType GetType() override; 18 | private: 19 | class BottomRetainer; 20 | }; 21 | 22 | } 23 | 24 | 25 | #endif // _WTF_BOTTOM_DANMAKU_HPP -------------------------------------------------------------------------------- /src/Controller.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Displayer.hpp" 3 | #include "DanmakusManager.hpp" 4 | #include "WinmmTimer.hpp" 5 | #include "PerformanceTimer.hpp" 6 | #include "Controller.hpp" 7 | 8 | namespace WTFDanmaku { 9 | 10 | Controller::Controller() { 11 | 12 | } 13 | 14 | Controller::~Controller() { 15 | Stop(); 16 | Terminate(); 17 | } 18 | 19 | int Controller::Initialize(void* hwnd, uint32_t initialWidth, uint32_t initialHeight) { 20 | if (hwnd == NULL && (initialWidth == 0 || initialHeight == 0)) 21 | return -1; 22 | 23 | mHwnd = hwnd; 24 | #ifdef _WTF_BUILD_UWP 25 | mTimer = PerformanceTimer::Create(); 26 | #else 27 | mTimer = WinmmTimer::Create(); 28 | #endif 29 | mManager = std::make_unique(); 30 | mManager->SetTimer(mTimer); 31 | 32 | mDisplayer = std::make_unique(); 33 | mDisplayer->SetTarget(mHwnd, initialWidth, initialHeight); 34 | 35 | bool succeed = mDisplayer->SetupBackend(); 36 | if (!succeed) { 37 | mHasBackend = false; 38 | return -1; 39 | } 40 | mHasBackend = true; 41 | return 0; 42 | } 43 | 44 | void Controller::Terminate() { 45 | if (mHasBackend) { 46 | mDisplayer->TeardownBackend(); 47 | mHasBackend = false; 48 | } 49 | } 50 | 51 | int Controller::QuerySwapChain(const void* pGuid, void** ppObject) { 52 | if (mDisplayer == nullptr) 53 | return -1; 54 | 55 | return mDisplayer->QuerySwapChain(pGuid, ppObject); 56 | } 57 | 58 | bool Controller::HasCommands() { 59 | std::lock_guard locker(mCommandQueueMutex); 60 | bool result = !mCommandQueue.empty(); 61 | return result; 62 | } 63 | 64 | void Controller::PushCommand(const Command& cmd) { 65 | std::lock_guard locker(mCommandQueueMutex); 66 | mCommandQueue.push(cmd); 67 | } 68 | 69 | Controller::Command Controller::PopCommand() { 70 | std::lock_guard locker(mCommandQueueMutex); 71 | Command cmd = mCommandQueue.front(); 72 | mCommandQueue.pop(); 73 | return cmd; 74 | } 75 | 76 | DanmakusManager* Controller::GetManager() { 77 | return mManager.get(); 78 | } 79 | 80 | bool Controller::IsRunning() { 81 | return mStatus == State::kRunning; 82 | } 83 | 84 | Controller::State Controller::GetState() { 85 | return mStatus; 86 | } 87 | 88 | void Controller::Start() { 89 | if (mStatus == State::kRunning) { 90 | // ignore 91 | } else if (mStatus == State::kPaused) { 92 | Resume(); 93 | } else { 94 | if (mWorker.joinable()) { 95 | mWorker.detach(); 96 | } 97 | mWorker = std::thread(&Controller::Working, this); 98 | } 99 | } 100 | 101 | void Controller::Pause() { 102 | if (mStatus == State::kRunning) { 103 | PushCommand(Cmd::kPause); 104 | } 105 | } 106 | 107 | void Controller::Resume() { 108 | if (mStatus == State::kPaused) { 109 | std::unique_lock locker(mConditionMutex); 110 | mStatus = State::kRunning; 111 | PushCommand(Cmd::kResume); 112 | mCondition.notify_all(); 113 | } 114 | } 115 | 116 | void Controller::Stop() { 117 | if (mStatus == State::kRunning || mStatus == State::kPaused) { 118 | if (mStatus == State::kPaused) { 119 | Resume(); 120 | } 121 | 122 | PushCommand(Cmd::kStop); 123 | if (mWorker.joinable()) { 124 | mWorker.join(); 125 | } 126 | } 127 | } 128 | 129 | void Controller::SeekTo(time_t milliseconds) { 130 | if (mStatus == State::kRunning || mStatus == State::kPaused) { 131 | Command cmd(Cmd::kSeek); 132 | cmd.arg1 = *reinterpret_cast(&milliseconds); 133 | cmd.arg2 = *(reinterpret_cast(&milliseconds) + 1); 134 | PushCommand(cmd); 135 | } 136 | } 137 | 138 | void Controller::Resize(uint32_t width, uint32_t height) { 139 | if (mStatus == State::kRunning || mStatus == State::kPaused) { 140 | Command cmd(Cmd::kResize); 141 | cmd.arg1 = width; 142 | cmd.arg2 = height; 143 | PushCommand(cmd); 144 | } 145 | } 146 | 147 | void Controller::SetDpi(uint32_t dpiX, uint32_t dpiY) { 148 | if (mStatus == State::kRunning || mStatus == State::kPaused) { 149 | Command cmd(Cmd::kSetDpi); 150 | cmd.arg1 = dpiX; 151 | cmd.arg2 = dpiY; 152 | PushCommand(cmd); 153 | } else { 154 | mDisplayer->SetDpi(dpiX, dpiY); 155 | } 156 | } 157 | 158 | void Controller::ReLayout() { 159 | if (mStatus == State::kRunning || mStatus == State::kPaused) { 160 | PushCommand(Cmd::kReLayout); 161 | } 162 | } 163 | 164 | time_t Controller::GetCurrentPosition() { 165 | return mTimer->GetMilliseconds(); 166 | } 167 | 168 | void Controller::HandleCommand() { 169 | if (!HasCommands()) 170 | return; 171 | 172 | while (HasCommands()) { 173 | Command cmd = PopCommand(); 174 | 175 | switch (cmd.what) { 176 | case Cmd::kResume: 177 | mTimer->Resume(); 178 | mStatus = State::kRunning; 179 | break; 180 | case Cmd::kPause: 181 | if (mStatus == State::kRunning) { 182 | mTimer->Pause(); 183 | std::unique_lock locker(mConditionMutex); 184 | mStatus = State::kPaused; 185 | while (mStatus == State::kPaused) { 186 | mCondition.wait(locker); 187 | } 188 | } 189 | break; 190 | case Cmd::kSeek: 191 | if (mStatus == State::kRunning) { 192 | time_t timepoint = 0; 193 | *reinterpret_cast(&timepoint) = cmd.arg1; 194 | *(reinterpret_cast(&timepoint) + 1) = cmd.arg2; 195 | mManager->SeekTo(timepoint); 196 | } 197 | break; 198 | case Cmd::kResize: 199 | mDisplayer->Resize(cmd.arg1, cmd.arg2); 200 | mManager->GetConfig()->MeasureFlag++; 201 | break; 202 | case Cmd::kReLayout: 203 | mManager->ReLayout(); 204 | break; 205 | case Cmd::kSetDpi: { 206 | float nowDpiX = mDisplayer->GetDpiX(); 207 | float nowDpiY = mDisplayer->GetDpiY(); 208 | if (cmd.arg1 != static_cast(nowDpiX) || cmd.arg2 != static_cast(nowDpiY)) { 209 | mDisplayer->SetDpi(cmd.arg1, cmd.arg2); 210 | mManager->GetConfig()->MeasureFlag++; 211 | mManager->GetConfig()->BitmapValidFlag++; 212 | mManager->ReLayout(); 213 | } 214 | } 215 | break; 216 | case Cmd::kStop: 217 | mStatus = State::kStopped; 218 | break; 219 | } 220 | } 221 | } 222 | 223 | void Controller::Working() { 224 | if (!mHasBackend) { 225 | #ifdef _WTF_BUILD_UWP 226 | #ifndef NDEBUG // Debug 227 | assert(false && "No backend for rendering, please Initialize first!"); 228 | #else // Release 229 | OutputDebugStringW(L"No backend for rendering, please Initialize first!"); 230 | throw 0; 231 | #endif 232 | #else 233 | DebugBreak(); 234 | #endif 235 | } 236 | 237 | mStatus = State::kRunning; 238 | mTimer->Start(); 239 | 240 | RenderingStatistics statistics; 241 | 242 | while (mStatus == State::kRunning || mStatus == State::kPaused) { 243 | HandleCommand(); 244 | if (mStatus == State::kStopped) { 245 | break; 246 | } 247 | 248 | statistics = mManager->DrawDanmakus(mDisplayer.get()); 249 | if (FAILED(statistics.lastHr)) { 250 | std::wstring message(L"Received HRESULT Error from DrawDanmakus, hr = "); 251 | message.append(std::to_wstring(statistics.lastHr)); 252 | message.append(L"\n"); 253 | OutputDebugStringW(message.c_str()); 254 | } 255 | } 256 | 257 | mTimer->Stop(); 258 | mManager->ReleaseActiveResources(); 259 | } 260 | 261 | } -------------------------------------------------------------------------------- /src/Controller.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_CONTROLLER_HPP 2 | #define _WTF_CONTROLLER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "BaseDanmaku.hpp" 11 | #include "Win32Mutex.hpp" 12 | #include "Noncopyable.hpp" 13 | #include "ITimer.hpp" 14 | 15 | namespace WTFDanmaku { 16 | 17 | class Displayer; 18 | class DanmakusManager; 19 | 20 | class Controller : public Noncopyable { 21 | public: 22 | enum class State : int { 23 | kIdle = 0, 24 | kRunning = 1, 25 | kPaused = 2, 26 | kStopped = 3 27 | }; 28 | private: 29 | enum class Cmd : int { 30 | kNull = 0, 31 | kBase = 0x12450, 32 | kStart = kBase + 1, 33 | kPause = kBase + 2, 34 | kResume = kBase + 3, 35 | kSeek = kBase + 4, 36 | kStop = kBase + 5, 37 | kResize = kBase + 6, 38 | kReLayout = kBase + 7, 39 | kSetDpi = kBase + 8 40 | }; 41 | 42 | struct Command { 43 | public: 44 | Cmd what = Cmd::kNull; 45 | int arg1 = 0; 46 | int arg2 = 0; 47 | Command() = default; 48 | Command(Cmd _what) : what(_what) {} 49 | }; 50 | public: 51 | explicit Controller(); 52 | ~Controller(); 53 | int Initialize(void* hwnd, uint32_t initialWidth = 0, uint32_t initialHeight = 0); 54 | void Terminate(); 55 | int QuerySwapChain(const void* pGuid, void** ppObject); 56 | DanmakusManager* GetManager(); 57 | void Start(); 58 | void Pause(); 59 | void Resume(); 60 | void Stop(); 61 | void SeekTo(time_t milliseconds); 62 | void Resize(uint32_t width, uint32_t height); 63 | void SetDpi(uint32_t dpiX, uint32_t dpiY); 64 | void ReLayout(); 65 | time_t GetCurrentPosition(); 66 | bool IsRunning(); 67 | State GetState(); 68 | private: 69 | void Working(); 70 | bool HasCommands(); 71 | void PushCommand(const Command& cmd); 72 | Command PopCommand(); 73 | void HandleCommand(); 74 | private: 75 | State mStatus = State::kIdle; 76 | std::thread mWorker; 77 | std::mutex mConditionMutex; 78 | std::condition_variable mCondition; 79 | void* mHwnd = nullptr; 80 | bool mHasBackend = false; 81 | TimerRef mTimer; 82 | Win32Mutex mCommandQueueMutex; 83 | std::queue mCommandQueue; 84 | std::unique_ptr mDisplayer; 85 | std::unique_ptr mManager; 86 | }; 87 | 88 | } 89 | 90 | #endif // _WTF_CONTROLLER_HPP -------------------------------------------------------------------------------- /src/DanmakuConfig.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DANMAKU_CONFIG_HPP 2 | #define _WTF_DANMAKU_CONFIG_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace WTFDanmaku { 8 | 9 | enum DanmakuStyle : int { 10 | kOutline = 1, 11 | kProjection = 2 12 | }; 13 | 14 | struct DanmakuConfig { 15 | bool R2LVisible = true; 16 | bool TopVisible = true; 17 | bool BottomVisible = true; 18 | 19 | int DanmakuDuration = 3800; 20 | 21 | int LogicalScreenWidth = 539; 22 | int LogicalScreenHeight = 385; 23 | 24 | int MeasureFlag = 0; 25 | int LayoutFlag = 0; 26 | int BitmapValidFlag = 0; 27 | 28 | float FontScaleFactor = 1.0f; 29 | float CompositionOpacity = 1.0f; 30 | 31 | DanmakuStyle DanmakuStyle = kOutline; 32 | 33 | std::wstring FontName = L"SimHei"; 34 | DWRITE_FONT_WEIGHT FontWeight = DWRITE_FONT_WEIGHT_BOLD; 35 | DWRITE_FONT_STYLE FontStyle = DWRITE_FONT_STYLE_NORMAL; 36 | DWRITE_FONT_STRETCH FontStretch = DWRITE_FONT_STRETCH_NORMAL; 37 | }; 38 | 39 | } 40 | 41 | #endif // _WTF_DANMAKU_CONFIG_HPP -------------------------------------------------------------------------------- /src/DanmakuFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "DanmakuFactory.hpp" 2 | #include "WinmmTimer.hpp" 3 | #include "PerformanceTimer.hpp" 4 | #include "R2LDanmaku.hpp" 5 | #include "TopDanmaku.hpp" 6 | #include "BottomDanmaku.hpp" 7 | #include "StringUtils.hpp" 8 | 9 | namespace WTFDanmaku { 10 | 11 | DanmakuRef DanmakuFactory::CreateDanmaku(DanmakuType type, time_t time, std::wstring& comment, int fontSize, int fontColor, time_t timestamp, int danmakuId) { 12 | DanmakuRef danmaku = nullptr; 13 | 14 | switch (type) { 15 | case kScrolling: 16 | danmaku = R2LDanmaku::Create(); 17 | break; 18 | case kTop: 19 | danmaku = TopDanmaku::Create(); 20 | break; 21 | case kBottom: 22 | danmaku = BottomDanmaku::Create(); 23 | break; 24 | case kNull: 25 | default: 26 | return nullptr; 27 | } 28 | 29 | if (timestamp == 0) { 30 | #ifdef _WTF_BUILD_UWP 31 | timestamp = PerformanceTimer::GetGlobalCurrent(); 32 | #else 33 | timestamp = WinmmTimer::GetGlobalCurrent(); 34 | #endif 35 | } 36 | 37 | danmaku->mStartTime = time; 38 | danmaku->mTimestamp = timestamp; 39 | danmaku->mDanmakuId = danmakuId; 40 | danmaku->mComment = comment; 41 | ReplaceStringInplace(danmaku->mComment, L"/n", L"\r\n"); 42 | danmaku->mTextSize = static_cast(fontSize); 43 | danmaku->mTextColor = fontColor | 0xFF000000; 44 | danmaku->mTextShadowColor = (danmaku->mTextColor <= 0xFF000000) ? 0xFFFFFFFF : 0xFF000000; 45 | 46 | return danmaku; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/DanmakuFactory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DANMAKU_FACTORY_HPP 2 | #define _WTF_DANMAKU_FACTORY_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Noncopyable.hpp" 8 | #include "BaseDanmaku.hpp" 9 | 10 | using std::shared_ptr; 11 | 12 | namespace WTFDanmaku { 13 | 14 | class DanmakuFactory : public Noncopyable { 15 | public: 16 | static DanmakuRef CreateDanmaku(DanmakuType type, time_t time, std::wstring& comment, int fontSize, int fontColor, time_t timestamp = 0, int danmakuId = 0); 17 | }; 18 | 19 | } 20 | 21 | #endif // _WTF_DANMAKU_FACTORY_HPP -------------------------------------------------------------------------------- /src/DanmakusManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Displayer.hpp" 3 | #include "DanmakusRetainer.hpp" 4 | #include "DanmakusManager.hpp" 5 | #include "Renderable.hpp" 6 | 7 | namespace WTFDanmaku { 8 | 9 | bool DanmakusManager::TimeComparator::operator() (const DanmakuRef& a, const DanmakuRef& b) { 10 | int64_t diff = 0; 11 | 12 | diff = a->GetStartTime() - b->GetStartTime(); 13 | if (diff < 0) 14 | return true; 15 | else if (diff > 0) 16 | return false; 17 | 18 | diff = a->GetSendTimestamp() - b->GetSendTimestamp(); 19 | if (diff < 0) 20 | return true; 21 | else if (diff > 0) 22 | return false; 23 | 24 | diff = a->GetDanmakuId() - b->GetDanmakuId(); 25 | if (diff < 0) 26 | return true; 27 | else if (diff > 0) 28 | return false; 29 | 30 | return true; 31 | } 32 | 33 | DanmakusManager::DanmakusManager() { 34 | mNextFetchIter = mAllDanmakus.begin(); 35 | mNextPrebuildIter = mAllDanmakus.begin(); 36 | mPrebuildBitmapValidFlag = mConfig.BitmapValidFlag; 37 | } 38 | 39 | DanmakusManager::~DanmakusManager() { 40 | ReleaseActiveResources(); 41 | mRetainer.Release(); 42 | mActiveDanmakus.clear(); 43 | mAllDanmakus.clear(); 44 | } 45 | 46 | void DanmakusManager::SetDanmakuList(unique_ptr> danmakuArray) { 47 | std::lock_guard locker(mAllDanmakusMutex); 48 | if (!mAllDanmakus.empty()) { 49 | mAllDanmakus.clear(); 50 | } 51 | for (auto iter = danmakuArray->begin(); iter != danmakuArray->end(); ++iter) { 52 | mAllDanmakus.insert(*iter); 53 | } 54 | mNextFetchIter = mAllDanmakus.begin(); 55 | mNextPrebuildIter = mAllDanmakus.begin(); 56 | } 57 | 58 | void DanmakusManager::SetTimer(TimerRef timer) { 59 | mTimer = timer; 60 | } 61 | 62 | void DanmakusManager::SeekTo(time_t timepoint) { 63 | std::lock_guard locker(mActiveDanmakusMutex); 64 | 65 | time_t current = mTimer->GetMilliseconds(); 66 | int64_t diff = timepoint - current; 67 | 68 | if (diff != 0) { 69 | mTimer->AddOffset(diff); 70 | if (diff < 0) { // seek back, reset cached iterator 71 | mActiveDanmakus.clear(); 72 | mNextFetchIter = mAllDanmakus.begin(); 73 | mNextPrebuildIter = mAllDanmakus.begin(); 74 | mForceFetch = true; 75 | } 76 | } 77 | } 78 | 79 | void DanmakusManager::AddDanmaku(DanmakuRef danmaku) { 80 | std::lock_guard locker(mAllDanmakusMutex); 81 | mAllDanmakus.insert(danmaku); 82 | } 83 | 84 | void DanmakusManager::AddLiveDanmaku(DanmakuRef danmaku) { 85 | std::lock_guard locker(mActiveDanmakusMutex); 86 | if (danmaku->GetStartTime() == 0) { 87 | danmaku->SetStartTime(mTimer->GetMilliseconds() + 50); 88 | } 89 | mActiveDanmakus.insert(danmaku); 90 | } 91 | 92 | void DanmakusManager::FetchNewDanmakus(Displayer* displayer) { 93 | std::lock_guard locker1(mAllDanmakusMutex); 94 | std::lock_guard locker2(mActiveDanmakusMutex); 95 | 96 | time_t current = mTimer->GetMilliseconds(); 97 | 98 | for (auto iter = mNextFetchIter; iter != mAllDanmakus.end(); /* ignore */) { 99 | if ((*iter)->GetStartTime() < current) { 100 | if (!(*iter)->HasMeasured(&mConfig)) { 101 | (*iter)->Measure(displayer, &mConfig); 102 | } 103 | if ((*iter)->IsAlive(current)) { 104 | mActiveDanmakus.insert(*iter); 105 | } 106 | } else { 107 | mNextFetchIter = iter; 108 | break; 109 | } 110 | 111 | if (++iter == mAllDanmakus.end()) { 112 | mNextFetchIter = mAllDanmakus.end(); 113 | break; 114 | } 115 | } 116 | 117 | mLastFetchTime = current; 118 | } 119 | 120 | void DanmakusManager::RemoveTimeoutDanmakus() { 121 | std::lock_guard locker(mActiveDanmakusMutex); 122 | 123 | time_t current = mTimer->GetMilliseconds(); 124 | 125 | for (auto iter = mActiveDanmakus.begin(); iter != mActiveDanmakus.end(); /* ignore */) { 126 | if ((*iter)->GetStartTime() < current && !(*iter)->IsAlive(current)) { 127 | (*iter)->SetSkipped(false); 128 | (*iter)->ReleaseResources(); 129 | iter = mActiveDanmakus.erase(iter); 130 | } else { 131 | ++iter; 132 | } 133 | } 134 | } 135 | 136 | void DanmakusManager::ReleaseActiveResources() { 137 | std::lock_guard locker(mActiveDanmakusMutex); 138 | 139 | mRetainer.Clear(); 140 | 141 | for (auto iter = mActiveDanmakus.begin(); iter != mActiveDanmakus.end(); /* ignore*/) { 142 | (*iter)->ReleaseResources(); 143 | iter = mActiveDanmakus.erase(iter); 144 | } 145 | } 146 | 147 | void DanmakusManager::ReLayout() { 148 | std::lock_guard locker(mActiveDanmakusMutex); 149 | 150 | mConfig.LayoutFlag++; 151 | mRetainer.Clear(); 152 | } 153 | 154 | void DanmakusManager::PrebuildRenderableTask(Displayer* displayer, time_t thisFrameTime, time_t remainTime) { 155 | if (mAllDanmakus.empty() || mNextPrebuildIter == mAllDanmakus.end()) { 156 | return; 157 | } 158 | 159 | if (mPrebuildBitmapValidFlag != mConfig.BitmapValidFlag) { 160 | mNextPrebuildIter = mAllDanmakus.begin(); 161 | mInPrebuildProgress = true; 162 | mPrebuildBitmapValidFlag = mConfig.BitmapValidFlag; 163 | } 164 | 165 | int64_t timepoint = thisFrameTime % 10000; // clamp time to [0ms, 9999ms], that is 0s ~ 10s 166 | time_t frameTimeBase = thisFrameTime - timepoint; 167 | if (mInPrebuildProgress == false) { 168 | if ((thisFrameTime >= 0 && thisFrameTime < 2000) || (timepoint >= 7000 && timepoint < 9000)) { 169 | mInPrebuildProgress = true; 170 | } else { 171 | return; 172 | } 173 | } 174 | 175 | std::lock_guard locker(mAllDanmakusMutex); 176 | 177 | const int kCurrentSection = 0; 178 | const int kNextSection = 1; 179 | int section = 0; 180 | 181 | if (timepoint < 7000) { // Task: Build cache for current time section 182 | section = kCurrentSection; 183 | time_t itemStartTime = (*mNextPrebuildIter)->GetStartTime(); 184 | if (itemStartTime >= frameTimeBase + 10000) { // Is it in next section? 185 | mInPrebuildProgress = false; // Current task has been completed. Just go back. 186 | return; 187 | } 188 | } else if (timepoint >= 7000 && timepoint < 9999) { // Task: Build cache for next 10-seconds time section 189 | section = kNextSection; 190 | time_t itemStartTime = (*mNextPrebuildIter)->GetStartTime(); 191 | if (itemStartTime >= frameTimeBase + 10000 + 10000) { 192 | mInPrebuildProgress = false; 193 | return; 194 | } 195 | } 196 | 197 | time_t beginTime = mTimer->GetMilliseconds(); 198 | 199 | for (auto iter = mNextPrebuildIter; iter != mAllDanmakus.end(); /* ignore */) { 200 | time_t itemStartTime = (*iter)->GetStartTime(); 201 | 202 | if (itemStartTime >= thisFrameTime) { 203 | if (section == kCurrentSection) { 204 | if (itemStartTime >= frameTimeBase + 10000) { 205 | mNextPrebuildIter = iter; 206 | mInPrebuildProgress = false; 207 | break; 208 | } 209 | } else if (section == kNextSection) { 210 | if (itemStartTime >= frameTimeBase + 10000 + 10000) { 211 | mNextPrebuildIter = iter; 212 | mInPrebuildProgress = false; 213 | break; 214 | } 215 | } 216 | 217 | if (!(*iter)->HasMeasured(&mConfig)) 218 | (*iter)->Measure(displayer, &mConfig); 219 | auto renderable = (*iter)->GetRenderable().lock(); 220 | if (!renderable->HasBitmap(&mConfig)) { 221 | renderable->BuildBitmap(displayer, &mConfig); 222 | } else { 223 | ++iter; continue; 224 | } 225 | } else if (itemStartTime < thisFrameTime) { // seeked iterator? 226 | if (!(*iter)->HasMeasured(&mConfig)) { 227 | (*iter)->Measure(displayer, &mConfig); 228 | } 229 | if ((*iter)->IsAlive(thisFrameTime)) { 230 | auto renderable = (*iter)->GetRenderable().lock(); 231 | if (!renderable->HasBitmap(&mConfig)) { 232 | renderable->BuildBitmap(displayer, &mConfig); 233 | } else { 234 | ++iter; continue; 235 | } 236 | } else { 237 | ++iter; continue; // fast-forward to thisFrameTime 238 | } 239 | } 240 | 241 | time_t now = mTimer->Update()->GetMilliseconds(); 242 | if (now - beginTime >= remainTime - 1) { 243 | mNextPrebuildIter = ++iter; // remain time excceed, save iterator and exit. 244 | break; 245 | } 246 | 247 | if (++iter == mAllDanmakus.end()) { 248 | mNextPrebuildIter = mAllDanmakus.end(); 249 | break; 250 | } 251 | } 252 | } 253 | 254 | bool DanmakusManager::IsVisibleDanmakuType(DanmakuRef danmaku, DanmakuConfig* config) { 255 | if (danmaku->IsSkipped()) { 256 | return false; 257 | } 258 | 259 | DanmakuType type = danmaku->GetType(); 260 | 261 | if (type == DanmakuType::kScrolling) { 262 | return config->R2LVisible; 263 | } else if (type == DanmakuType::kTop) { 264 | return config->TopVisible; 265 | } else if (type == DanmakuType::kBottom) { 266 | return config->BottomVisible; 267 | } 268 | 269 | return true; 270 | } 271 | 272 | RenderingStatistics DanmakusManager::DrawDanmakus(Displayer* displayer) { 273 | mTimer->Update(); 274 | time_t current = mTimer->GetMilliseconds(); 275 | 276 | if (mForceFetch || current - mLastFetchTime >= 100) { 277 | RemoveTimeoutDanmakus(); 278 | FetchNewDanmakus(displayer); 279 | mForceFetch = false; 280 | } 281 | 282 | int count = 0; 283 | HRESULT hr = S_OK; 284 | 285 | /* synchronized */ 286 | { 287 | std::lock_guard locker(mActiveDanmakusMutex); 288 | 289 | displayer->BeginDraw(); 290 | 291 | for (auto iter = mActiveDanmakus.begin(); iter != mActiveDanmakus.end(); ++iter, ++count) { 292 | if (!(*iter)->HasMeasured(&mConfig)) { 293 | (*iter)->Measure(displayer, &mConfig); 294 | } 295 | if (!(*iter)->HasLayout(&mConfig)) { 296 | mRetainer.Add(*iter, displayer, &mConfig, current); 297 | } 298 | if (IsVisibleDanmakuType(*iter, &mConfig)) { 299 | displayer->DrawDanmakuItem(*iter, current, &mConfig); 300 | } 301 | } 302 | 303 | hr = displayer->EndDraw(); 304 | 305 | time_t now = mTimer->Update()->GetMilliseconds(); 306 | int64_t elapsed = now - current; 307 | if (elapsed < 14) { 308 | time_t remain = 14 - elapsed; 309 | PrebuildRenderableTask(displayer, current, remain); 310 | } 311 | } 312 | 313 | hr = displayer->Present(); 314 | 315 | mStatistics.lastHr = hr; 316 | mStatistics.lastFrameDanmakuCount = count; 317 | mStatistics.lastFrameTime = current; 318 | 319 | return mStatistics; 320 | } 321 | 322 | RenderingStatistics DanmakusManager::GetRenderingStatistics() { 323 | return mStatistics; 324 | } 325 | 326 | DanmakuConfig* DanmakusManager::GetConfig() { 327 | return &mConfig; 328 | } 329 | 330 | } -------------------------------------------------------------------------------- /src/DanmakusManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DANMAKUS_MANAGER_HPP 2 | #define _WTF_DANMAKUS_MANAGER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "BaseDanmaku.hpp" 9 | #include "Noncopyable.hpp" 10 | #include "DanmakusRetainer.hpp" 11 | #include "DanmakuConfig.hpp" 12 | #include "Win32Mutex.hpp" 13 | #include "ITimer.hpp" 14 | 15 | using std::unique_ptr; 16 | 17 | namespace WTFDanmaku { 18 | 19 | class Displayer; 20 | class DanmakusRetainer; 21 | 22 | struct RenderingStatistics { 23 | public: 24 | float fps = 0.0f; 25 | time_t lastFrameTime = 0; 26 | int lastFrameDanmakuCount = 0; 27 | HRESULT lastHr = NULL; 28 | }; 29 | 30 | class DanmakusManager : public Noncopyable { 31 | public: 32 | explicit DanmakusManager(); 33 | ~DanmakusManager(); 34 | void SetDanmakuList(unique_ptr> danmakuArray); 35 | void SetTimer(TimerRef timer); 36 | void SeekTo(time_t timepoint); 37 | void AddDanmaku(DanmakuRef danmaku); 38 | void AddLiveDanmaku(DanmakuRef danmaku); 39 | void ReleaseActiveResources(); 40 | void ReLayout(); 41 | DanmakuConfig* GetConfig(); 42 | RenderingStatistics DrawDanmakus(Displayer* displayer); 43 | RenderingStatistics GetRenderingStatistics(); 44 | private: 45 | void FetchNewDanmakus(Displayer* displayer); 46 | void RemoveTimeoutDanmakus(); 47 | void PrebuildRenderableTask(Displayer* displayer, time_t thisFrameTime, time_t remainTime); 48 | bool IsVisibleDanmakuType(DanmakuRef danmaku, DanmakuConfig* config); 49 | private: 50 | struct TimeComparator { 51 | bool operator() (const DanmakuRef& a, const DanmakuRef& b); 52 | }; 53 | typedef std::set TimeSortedDanmakus; 54 | private: 55 | TimerRef mTimer; 56 | bool mForceFetch = false; 57 | bool mInPrebuildProgress = false; 58 | time_t mLastFetchTime = 0; 59 | int mPrebuildBitmapValidFlag = -1; 60 | TimeSortedDanmakus::iterator mNextFetchIter; 61 | TimeSortedDanmakus::iterator mNextPrebuildIter; 62 | Win32Mutex mAllDanmakusMutex; 63 | Win32Mutex mActiveDanmakusMutex; 64 | TimeSortedDanmakus mAllDanmakus; 65 | TimeSortedDanmakus mActiveDanmakus; 66 | DanmakusRetainer mRetainer; 67 | DanmakuConfig mConfig; 68 | RenderingStatistics mStatistics; 69 | }; 70 | 71 | } 72 | 73 | #endif // _WTF_DANMAKUS_MANAGER_HPP -------------------------------------------------------------------------------- /src/DanmakusRetainer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "BaseDanmaku.hpp" 5 | #include "R2LDanmaku.hpp" 6 | #include "TopDanmaku.hpp" 7 | #include "BottomDanmaku.hpp" 8 | #include "Displayer.hpp" 9 | #include "DanmakusRetainer.hpp" 10 | 11 | namespace WTFDanmaku { 12 | 13 | void DanmakusRetainer::Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis) { 14 | switch (danmaku->GetType()) { 15 | case DanmakuType::kScrolling: 16 | if (mR2LRetainer == nullptr) { 17 | mR2LRetainer = R2LDanmaku::CreateRetainer(); 18 | } 19 | mR2LRetainer->Add(danmaku, displayer, config, currentMillis); 20 | break; 21 | case DanmakuType::kTop: 22 | if (mTopRetainer == nullptr) { 23 | mTopRetainer = TopDanmaku::CreateRetainer(); 24 | } 25 | mTopRetainer->Add(danmaku, displayer, config, currentMillis); 26 | break; 27 | case DanmakuType::kBottom: 28 | if (mBottomRetainer == nullptr) { 29 | mBottomRetainer = BottomDanmaku::CreateRetainer(); 30 | } 31 | mBottomRetainer->Add(danmaku, displayer, config, currentMillis); 32 | break; 33 | default: 34 | break; 35 | } 36 | } 37 | 38 | void DanmakusRetainer::Clear() { 39 | if (mR2LRetainer != nullptr) 40 | mR2LRetainer->Clear(); 41 | if (mTopRetainer != nullptr) 42 | mTopRetainer->Clear(); 43 | if (mBottomRetainer != nullptr) 44 | mBottomRetainer->Clear(); 45 | } 46 | 47 | void DanmakusRetainer::Release() { 48 | Clear(); 49 | mR2LRetainer.reset(); 50 | mTopRetainer.reset(); 51 | mBottomRetainer.reset(); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/DanmakusRetainer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DANMAKU_RETAINER_HPP 2 | #define _WTF_DANMAKU_RETAINER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Noncopyable.hpp" 9 | #include "BaseDanmaku.hpp" 10 | 11 | using std::unique_ptr; 12 | 13 | namespace WTFDanmaku { 14 | 15 | class Displayer; 16 | 17 | class IDanmakusRetainer : public Noncopyable { 18 | protected: 19 | typedef std::map> Danmakus; 20 | typedef std::map> DecDanmakus; 21 | public: 22 | IDanmakusRetainer() = default; 23 | virtual ~IDanmakusRetainer() = default; 24 | virtual void Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis) = 0; 25 | virtual void Clear() = 0; 26 | virtual void Release() = 0; 27 | }; 28 | 29 | class DanmakusRetainer : public Noncopyable { 30 | public: 31 | void Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis); 32 | void Clear(); 33 | void Release(); 34 | private: 35 | unique_ptr mR2LRetainer = nullptr; 36 | unique_ptr mTopRetainer = nullptr; 37 | unique_ptr mBottomRetainer = nullptr; 38 | }; 39 | 40 | } 41 | 42 | #endif // _WTF_DANMAKU_RETAINER_HPP -------------------------------------------------------------------------------- /src/Displayer.cpp: -------------------------------------------------------------------------------- 1 | #include "DisplayerImpl.hpp" 2 | #include "Displayer.hpp" 3 | 4 | namespace WTFDanmaku { 5 | 6 | Displayer::Displayer() : pImpl(new DisplayerImpl(this)) { } 7 | 8 | Displayer::~Displayer() { 9 | pImpl.reset(); 10 | } 11 | 12 | void Displayer::SetTarget(void* windowHandle, uint32_t initialWidth, uint32_t initialHeight) { 13 | pImpl->SetTarget(static_cast(windowHandle), initialWidth, initialHeight); 14 | } 15 | 16 | bool Displayer::SetupBackend() { 17 | return pImpl->SetupBackend(); 18 | } 19 | 20 | bool Displayer::TeardownBackend() { 21 | return pImpl->TeardownBackend(); 22 | } 23 | 24 | int Displayer::QuerySwapChain(const void* pGuid, void** ppObject) { 25 | return pImpl->QuerySwapChain(reinterpret_cast(pGuid), ppObject); 26 | } 27 | 28 | int Displayer::GetWidth() { 29 | return pImpl->GetWidth(); 30 | } 31 | 32 | int Displayer::GetHeight() { 33 | return pImpl->GetHeight(); 34 | } 35 | 36 | float Displayer::GetDpiX() { 37 | return pImpl->GetDpiX(); 38 | } 39 | 40 | float Displayer::GetDpiY() { 41 | return pImpl->GetDpiY(); 42 | } 43 | 44 | void Displayer::Resize(uint32_t width, uint32_t height) { 45 | pImpl->Resize(width, height); 46 | } 47 | 48 | void Displayer::SetDpi(uint32_t dpiX, uint32_t dpiY) { 49 | pImpl->SetDpi(dpiX, dpiY); 50 | } 51 | 52 | ComPtr Displayer::CreateBitmap(uint32_t width, uint32_t height) { 53 | return pImpl->CreateBitmap(width, height); 54 | } 55 | 56 | ComPtr Displayer::AcquireDeviceContext(ComPtr bitmap) { 57 | return pImpl->AcquireDeviceContext(bitmap); 58 | } 59 | 60 | void Displayer::ReleaseDeviceContext(ComPtr deviceContext) { 61 | pImpl->ReleaseDeviceContext(deviceContext); 62 | } 63 | 64 | ComPtr Displayer::GetD2DFactory() { 65 | return pImpl->GetD2DFactory(); 66 | } 67 | 68 | ComPtr Displayer::GetDWriteFactory() { 69 | return pImpl->GetDWriteFactory(); 70 | } 71 | 72 | void Displayer::DrawDanmakuItem(DanmakuRef item, time_t current, DanmakuConfig* config) { 73 | pImpl->DrawDanmakuItem(item, current, config); 74 | } 75 | 76 | void Displayer::BeginDraw() { 77 | pImpl->BeginDraw(); 78 | } 79 | 80 | HRESULT Displayer::EndDraw() { 81 | return pImpl->EndDraw(); 82 | } 83 | 84 | HRESULT Displayer::Present() { 85 | return pImpl->Present(); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/Displayer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DISPLAYER_HPP 2 | #define _WTF_DISPLAYER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "BaseDanmaku.hpp" 10 | #include "Noncopyable.hpp" 11 | 12 | using Microsoft::WRL::ComPtr; 13 | 14 | namespace WTFDanmaku { 15 | 16 | class DisplayerImpl; 17 | struct DanmakuConfig; 18 | 19 | class Displayer : public Noncopyable { 20 | public: 21 | explicit Displayer(); 22 | ~Displayer(); 23 | void SetTarget(void* windowHandle, uint32_t initialWidth = 0, uint32_t initialHeight = 0); 24 | bool SetupBackend(); 25 | bool TeardownBackend(); 26 | int QuerySwapChain(const void* pGuid, void** ppObject); 27 | int GetWidth(); 28 | int GetHeight(); 29 | float GetDpiX(); 30 | float GetDpiY(); 31 | void Resize(uint32_t width, uint32_t height); 32 | void SetDpi(uint32_t dpiX, uint32_t dpiY); 33 | ComPtr CreateBitmap(uint32_t width, uint32_t height); 34 | ComPtr AcquireDeviceContext(ComPtr bitmap); 35 | void ReleaseDeviceContext(ComPtr deviceContext); 36 | void DrawDanmakuItem(DanmakuRef item, time_t current, DanmakuConfig* config); 37 | void BeginDraw(); 38 | HRESULT EndDraw(); 39 | HRESULT Present(); 40 | ComPtr GetD2DFactory(); 41 | ComPtr GetDWriteFactory(); 42 | private: 43 | std::unique_ptr pImpl; 44 | }; 45 | 46 | } 47 | 48 | #endif // _WTF_DISPLAYER_HPP -------------------------------------------------------------------------------- /src/DisplayerImpl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_DISPLAYER_IMPL_HPP 2 | #define _WTF_DISPLAYER_IMPL_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #if !defined(_WTF_BUILD_WIN7) && !defined(_WTF_BUILD_UWP) 10 | #include 11 | #endif 12 | #include 13 | #include "Win32Mutex.hpp" 14 | #include "Noncopyable.hpp" 15 | #include "BaseDanmaku.hpp" 16 | 17 | using Microsoft::WRL::ComPtr; 18 | 19 | namespace WTFDanmaku { 20 | 21 | class Displayer; 22 | struct DanmakuConfig; 23 | 24 | class DisplayerImpl : public Noncopyable { 25 | public: 26 | explicit DisplayerImpl(Displayer* outer); 27 | ~DisplayerImpl(); 28 | void SetTarget(HWND windowHandle, uint32_t initialWidth = 0, uint32_t initialHeight = 0); 29 | bool SetupBackend(); 30 | bool TeardownBackend(); 31 | HRESULT QuerySwapChain(const IID* pGuid, void** ppvObject); 32 | void Resize(uint32_t width, uint32_t height); 33 | void SetDpi(uint32_t dpiX, uint32_t dpiY); 34 | ComPtr CreateBitmap(uint32_t width, uint32_t height); 35 | ComPtr AcquireDeviceContext(ComPtr bitmap); 36 | void ReleaseDeviceContext(ComPtr deviceContext); 37 | void DrawDanmakuItem(DanmakuRef item, time_t current, DanmakuConfig* config); 38 | void BeginDraw(); 39 | HRESULT EndDraw(); 40 | HRESULT Present(); 41 | ComPtr GetD2DFactory(); 42 | ComPtr GetDWriteFactory(); 43 | public: 44 | inline int GetWidth() { 45 | return mWidth; 46 | } 47 | 48 | inline int GetHeight() { 49 | return mHeight; 50 | } 51 | 52 | inline float GetDpiX() { 53 | return mDpiX; 54 | } 55 | 56 | inline float GetDpiY() { 57 | return mDpiY; 58 | } 59 | private: 60 | static HRESULT CreateD3D11Device(IDXGIAdapter* adapter, D3D_DRIVER_TYPE driverType, UINT flags, 61 | ID3D11Device** ppDevice, ID3D11DeviceContext** ppDevCtx, D3D_FEATURE_LEVEL* resultLevel); 62 | HRESULT CreateDeviceIndependentResources(); 63 | HRESULT CreateDeviceResources(); 64 | HRESULT CreateTargetDependentResources(); 65 | HRESULT CreateDCompResources(); 66 | HRESULT HandleDeviceLost(); 67 | private: 68 | HWND mHwnd = 0; 69 | int mWidth = 0; 70 | int mHeight = 0; 71 | float mDpiX = 0.0f; 72 | float mDpiY = 0.0f; 73 | bool mHasBackend = false; 74 | Displayer* mOuter = nullptr; 75 | Win32Mutex mRenderMutex; 76 | Win32Mutex mLendMutex; 77 | bool mInRendering = false; 78 | bool mNeedRecreateBitmap = false; 79 | D3D_FEATURE_LEVEL mCurrentFeatureLevel; 80 | ComPtr mD3DDevice; 81 | ComPtr mD3DDeviceContext; 82 | ComPtr mDxgiFactory; 83 | ComPtr mDxgiDevice; 84 | ComPtr mDxgiSurface; 85 | ComPtr mSurfaceBitmap; 86 | ComPtr mSwapChain; 87 | ComPtr mDWriteFactory; 88 | ComPtr mD2DFactory; 89 | ComPtr mD2DDevice; 90 | ComPtr mDeviceContext; 91 | ComPtr mLendContext; 92 | #if !defined(_WTF_BUILD_WIN7) && !defined(_WTF_BUILD_UWP) 93 | ComPtr mDCompDevice; 94 | ComPtr mDCompTarget; 95 | ComPtr mDCompVisual; 96 | #endif 97 | }; 98 | 99 | } 100 | 101 | #endif // _WTF_DISPLAYER_IMPL_HPP -------------------------------------------------------------------------------- /src/IParser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_IPARSER_HPP 2 | #define _WTF_IPARSER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "BaseDanmaku.hpp" 8 | #include "Noncopyable.hpp" 9 | 10 | namespace WTFDanmaku { 11 | 12 | class IParser : public Noncopyable { 13 | public: 14 | explicit IParser() = default; 15 | virtual ~IParser() = default; 16 | virtual bool ParseStringSource(const char* str) = 0; 17 | virtual bool ParseFileSource(const char* filePath) = 0; 18 | virtual bool ParseFileSource(const wchar_t* filePath) = 0; 19 | virtual std::unique_ptr> GetDanmakus() = 0; 20 | }; 21 | 22 | typedef std::shared_ptr ParserRef; 23 | 24 | } 25 | 26 | #endif // _WTF_IPARSER_HPP -------------------------------------------------------------------------------- /src/ITimer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_TIMER_HPP 2 | #define _WTF_TIMER_HPP 3 | 4 | #include 5 | #include 6 | #include "Noncopyable.hpp" 7 | 8 | using std::shared_ptr; 9 | 10 | namespace WTFDanmaku { 11 | 12 | class ITimer : public Noncopyable { 13 | public: 14 | ITimer() = default; 15 | virtual ~ITimer() = default; 16 | virtual void Start() = 0; 17 | virtual void Pause() = 0; 18 | virtual void Resume() = 0; 19 | virtual ITimer* Update() = 0; 20 | virtual void Stop() = 0; 21 | virtual void AddOffset(int64_t offset) = 0; 22 | virtual time_t GetMilliseconds() = 0; 23 | }; 24 | 25 | typedef shared_ptr TimerRef; 26 | 27 | } 28 | 29 | #endif // _WTF_TIMER_HPP -------------------------------------------------------------------------------- /src/Noncopyable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_NONCOPYABLE_HPP 2 | #define _WTF_NONCOPYABLE_HPP 3 | 4 | namespace WTFDanmaku { 5 | 6 | class Noncopyable { 7 | public: 8 | explicit Noncopyable() = default; 9 | ~Noncopyable() = default; 10 | private: 11 | explicit Noncopyable(const Noncopyable&) = delete; 12 | Noncopyable& operator=(const Noncopyable&) = delete; 13 | }; 14 | 15 | } 16 | 17 | #endif // _WTF_NONCOPYABLE_HPP -------------------------------------------------------------------------------- /src/OutlineTextRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "OutlineTextRenderer.hpp" 2 | 3 | namespace WTFDanmaku { 4 | 5 | OutlineTextRenderer::OutlineTextRenderer( 6 | ComPtr d2dFactory, 7 | ComPtr renderTarget, 8 | ComPtr outlineBrush, 9 | float outlineStrokeWidth, 10 | ComPtr fillBrush 11 | ) : 12 | mRefCount(0), 13 | mD2DFactory(d2dFactory), 14 | mRenderTarget(renderTarget), 15 | mOutlineBrush(outlineBrush), 16 | mOutlineStrokeWidth(outlineStrokeWidth), 17 | mFillBrush(fillBrush) 18 | { 19 | 20 | } 21 | 22 | IFACEMETHODIMP OutlineTextRenderer::DrawGlyphRun( 23 | __maybenull void* clientDrawingContext, 24 | float baselineOriginX, 25 | float baselineOriginY, 26 | DWRITE_MEASURING_MODE measuringMode, 27 | __in DWRITE_GLYPH_RUN const* glyphRun, 28 | __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 29 | IUnknown* clientDrawingEffect 30 | ) 31 | { 32 | HRESULT hr = S_OK; 33 | 34 | ComPtr pathGeometry; 35 | hr = mD2DFactory->CreatePathGeometry(&pathGeometry); 36 | 37 | ComPtr sink; 38 | if (SUCCEEDED(hr)) { 39 | hr = pathGeometry->Open(&sink); 40 | } 41 | 42 | if (SUCCEEDED(hr)) { 43 | hr = glyphRun->fontFace->GetGlyphRunOutline( 44 | glyphRun->fontEmSize, 45 | glyphRun->glyphIndices, 46 | glyphRun->glyphAdvances, 47 | glyphRun->glyphOffsets, 48 | glyphRun->glyphCount, 49 | glyphRun->isSideways, 50 | glyphRun->bidiLevel % 2, 51 | sink.Get() 52 | ); 53 | } 54 | 55 | if (SUCCEEDED(hr)) { 56 | hr = sink->Close(); 57 | } 58 | 59 | const D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F( 60 | 1.0f, 0.0f, 61 | 0.0f, 1.0f, 62 | baselineOriginX, baselineOriginY 63 | ); 64 | 65 | ComPtr transformedGeometry; 66 | if (SUCCEEDED(hr)) { 67 | hr = mD2DFactory->CreateTransformedGeometry(pathGeometry.Get(), matrix, &transformedGeometry); 68 | } 69 | 70 | mRenderTarget->DrawGeometry(transformedGeometry.Get(), mOutlineBrush.Get(), mOutlineStrokeWidth); 71 | mRenderTarget->FillGeometry(transformedGeometry.Get(), mFillBrush.Get()); 72 | 73 | return hr; 74 | } 75 | 76 | IFACEMETHODIMP OutlineTextRenderer::DrawUnderline( 77 | __maybenull void* clientDrawingContext, 78 | float baselineOriginX, 79 | float baselineOriginY, 80 | __in DWRITE_UNDERLINE const* underline, 81 | IUnknown* clientDrawingEffect 82 | ) 83 | { 84 | HRESULT hr; 85 | 86 | D2D1_RECT_F rect = D2D1::RectF( 87 | 0, 88 | underline->offset, 89 | underline->width, 90 | underline->offset + underline->thickness 91 | ); 92 | 93 | ComPtr rectangleGeometry; 94 | hr = mD2DFactory->CreateRectangleGeometry(rect, &rectangleGeometry); 95 | 96 | const D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F( 97 | 1.0f, 0.0f, 98 | 0.0f, 1.0f, 99 | baselineOriginX, baselineOriginY 100 | ); 101 | 102 | ComPtr transformedGeometry; 103 | if (SUCCEEDED(hr)) { 104 | hr = mD2DFactory->CreateTransformedGeometry(rectangleGeometry.Get(), matrix, &transformedGeometry); 105 | } 106 | 107 | mRenderTarget->DrawGeometry(transformedGeometry.Get(), mOutlineBrush.Get(), mOutlineStrokeWidth); 108 | mRenderTarget->FillGeometry(transformedGeometry.Get(), mFillBrush.Get()); 109 | 110 | return S_OK; 111 | } 112 | 113 | IFACEMETHODIMP OutlineTextRenderer::DrawStrikethrough( 114 | __maybenull void* clientDrawingContext, 115 | float baselineOriginX, 116 | float baselineOriginY, 117 | __in DWRITE_STRIKETHROUGH const* strikeThrough, 118 | IUnknown* clientDrawingEffect 119 | ) 120 | { 121 | HRESULT hr; 122 | 123 | D2D1_RECT_F rect = D2D1::RectF( 124 | 0, 125 | strikeThrough->offset, 126 | strikeThrough->width, 127 | strikeThrough->offset + strikeThrough->thickness 128 | ); 129 | 130 | ComPtr rectangleGeometry; 131 | hr = mD2DFactory->CreateRectangleGeometry(rect, &rectangleGeometry); 132 | 133 | const D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F( 134 | 1.0f, 0.0f, 135 | 0.0f, 1.0f, 136 | baselineOriginX, baselineOriginY 137 | ); 138 | 139 | ComPtr transformedGeometry; 140 | if (SUCCEEDED(hr)) { 141 | hr = mD2DFactory->CreateTransformedGeometry(rectangleGeometry.Get(), matrix, &transformedGeometry); 142 | } 143 | 144 | mRenderTarget->DrawGeometry(transformedGeometry.Get(), mOutlineBrush.Get(), mOutlineStrokeWidth); 145 | mRenderTarget->FillGeometry(transformedGeometry.Get(), mFillBrush.Get()); 146 | 147 | return S_OK; 148 | } 149 | 150 | IFACEMETHODIMP OutlineTextRenderer::DrawInlineObject( 151 | __maybenull void* clientDrawingContext, 152 | float originX, 153 | float originY, 154 | IDWriteInlineObject* inlineObject, 155 | BOOL isSideways, 156 | BOOL isRightToLeft, 157 | IUnknown* clientDrawingEffect 158 | ) 159 | { 160 | return E_NOTIMPL; 161 | } 162 | 163 | IFACEMETHODIMP OutlineTextRenderer::IsPixelSnappingDisabled( 164 | __maybenull void* clientDrawingContext, 165 | __out BOOL* isDisabled 166 | ) 167 | { 168 | *isDisabled = FALSE; 169 | return S_OK; 170 | } 171 | 172 | IFACEMETHODIMP OutlineTextRenderer::GetCurrentTransform( 173 | __maybenull void* clientDrawingContext, 174 | __out DWRITE_MATRIX* transform 175 | ) 176 | { 177 | mRenderTarget->GetTransform(reinterpret_cast(transform)); 178 | return S_OK; 179 | } 180 | 181 | IFACEMETHODIMP OutlineTextRenderer::GetPixelsPerDip( 182 | __maybenull void* clientDrawingContext, 183 | __out FLOAT* pixelsPreDip 184 | ) 185 | { 186 | float x, y; 187 | mRenderTarget->GetDpi(&x, &y); 188 | *pixelsPreDip = x / 96; 189 | 190 | return S_OK; 191 | } 192 | 193 | IFACEMETHODIMP_(unsigned long) OutlineTextRenderer::AddRef() { 194 | return InterlockedIncrement(&mRefCount); 195 | } 196 | 197 | IFACEMETHODIMP_(unsigned long) OutlineTextRenderer::Release() { 198 | unsigned long result = InterlockedDecrement(&mRefCount); 199 | if (result == 0) { 200 | delete this; 201 | return 0; 202 | } 203 | return result; 204 | } 205 | 206 | IFACEMETHODIMP OutlineTextRenderer::QueryInterface(IID const& riid, void** ppvObject) { 207 | if (__uuidof(IDWriteTextRenderer) == riid) { 208 | *ppvObject = this; 209 | } else if (__uuidof(IDWritePixelSnapping) == riid) { 210 | *ppvObject = this; 211 | } else if (__uuidof(IUnknown) == riid) { 212 | *ppvObject = this; 213 | } else { 214 | *ppvObject = nullptr; 215 | return E_FAIL; 216 | } 217 | 218 | this->AddRef(); 219 | return S_OK; 220 | } 221 | 222 | } -------------------------------------------------------------------------------- /src/OutlineTextRenderer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_OUTLINE_TEXT_RENDERER_HPP 2 | #define _WTF_OUTLINE_TEXT_RENDERER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using Microsoft::WRL::ComPtr; 10 | 11 | namespace WTFDanmaku { 12 | 13 | class OutlineTextRenderer : public IDWriteTextRenderer { 14 | public: 15 | explicit OutlineTextRenderer( 16 | ComPtr d2dFactory, 17 | ComPtr renderTarget, 18 | ComPtr outlineBrush, 19 | float outlineStrokeWidth, 20 | ComPtr fillBrush 21 | ); 22 | 23 | virtual ~OutlineTextRenderer() = default; 24 | 25 | IFACEMETHOD(IsPixelSnappingDisabled)( 26 | __maybenull void* clientDrawingContext, 27 | __out BOOL* isDisabled 28 | ); 29 | 30 | IFACEMETHOD(GetCurrentTransform)( 31 | __maybenull void* clientDrawingContext, 32 | __out DWRITE_MATRIX* transform 33 | ); 34 | 35 | IFACEMETHOD(GetPixelsPerDip)( 36 | __maybenull void* clientDrawingContext, 37 | __out FLOAT* pixelsPreDip 38 | ); 39 | 40 | IFACEMETHOD(DrawGlyphRun)( 41 | __maybenull void* clientDrawingContext, 42 | float baselineOriginX, 43 | float baselineOriginY, 44 | DWRITE_MEASURING_MODE measuringMode, 45 | __in DWRITE_GLYPH_RUN const* glyphRun, 46 | __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 47 | IUnknown* clientDrawingEffect 48 | ); 49 | 50 | IFACEMETHOD(DrawUnderline)( 51 | __maybenull void* clientDrawingContext, 52 | float baselineOriginX, 53 | float baselineOriginY, 54 | __in DWRITE_UNDERLINE const* underline, 55 | IUnknown* clientDrawingEffect 56 | ); 57 | 58 | IFACEMETHOD(DrawStrikethrough)( 59 | __maybenull void* clientDrawingContext, 60 | float baselineOriginX, 61 | float baselineOriginY, 62 | __in DWRITE_STRIKETHROUGH const* strikeThrough, 63 | IUnknown* clientDrawingEffect 64 | ); 65 | 66 | IFACEMETHOD(DrawInlineObject)( 67 | __maybenull void* clientDrawingContext, 68 | float originX, 69 | float originY, 70 | IDWriteInlineObject* inlineObject, 71 | BOOL isSideways, 72 | BOOL isRightToLeft, 73 | IUnknown* clientDrawingEffect 74 | ); 75 | public: 76 | IFACEMETHOD_(unsigned long, AddRef) (); 77 | IFACEMETHOD_(unsigned long, Release) (); 78 | IFACEMETHOD(QueryInterface)( 79 | IID const& riid, 80 | void** ppvObject 81 | ); 82 | private: 83 | unsigned long mRefCount; 84 | ComPtr mD2DFactory; 85 | ComPtr mRenderTarget; 86 | ComPtr mOutlineBrush; 87 | float mOutlineStrokeWidth; 88 | ComPtr mFillBrush; 89 | }; 90 | 91 | } 92 | 93 | #endif // _WTF_OUTLINE_TEXT_RENDERER_HPP -------------------------------------------------------------------------------- /src/PerformanceTimer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "PerformanceTimer.hpp" 3 | 4 | namespace WTFDanmaku { 5 | 6 | time_t PerformanceTimer::GetGlobalCurrent() { 7 | LARGE_INTEGER frequency, current; 8 | QueryPerformanceFrequency(&frequency); 9 | QueryPerformanceCounter(¤t); 10 | return current.QuadPart * 1000 / frequency.QuadPart; 11 | } 12 | 13 | void PerformanceTimer::Start() { 14 | mTimeBase = 0; 15 | Resume(); 16 | } 17 | 18 | void PerformanceTimer::Pause() { 19 | LARGE_INTEGER current; 20 | QueryPerformanceCounter(¤t); 21 | int64_t milliseconds = (current.QuadPart - mBeginCounter.QuadPart) * 1000 / mFrequency.QuadPart; 22 | mTimeBase += milliseconds; 23 | } 24 | 25 | void PerformanceTimer::Resume() { 26 | QueryPerformanceFrequency(reinterpret_cast(&mFrequency)); 27 | QueryPerformanceCounter(reinterpret_cast(&mBeginCounter)); 28 | } 29 | 30 | ITimer* PerformanceTimer::Update() { 31 | LARGE_INTEGER current; 32 | QueryPerformanceCounter(¤t); 33 | int64_t milliseconds = (current.QuadPart - mBeginCounter.QuadPart) * 1000 / mFrequency.QuadPart; 34 | mCurrent = milliseconds + mTimeBase; 35 | return this; 36 | } 37 | 38 | void PerformanceTimer::Stop() { 39 | mBeginCounter.QuadPart = 0; 40 | mTimeBase = 0; 41 | } 42 | 43 | void PerformanceTimer::AddOffset(int64_t offset) { 44 | mTimeBase += offset; 45 | } 46 | 47 | time_t PerformanceTimer::GetMilliseconds() { 48 | return mCurrent; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/PerformanceTimer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_PERFORMANCE_TIMER_HPP 2 | #define _WTF_PERFORMANCE_TIMER_HPP 3 | 4 | #include "ITimer.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | typedef union _LargeInteger { 9 | struct { 10 | uint32_t LowPart; 11 | int32_t HighPart; 12 | }; 13 | int64_t QuadPart; 14 | } LargeInteger; 15 | 16 | class PerformanceTimer : public ITimer { 17 | public: 18 | static inline TimerRef Create() { 19 | return std::make_shared(); 20 | } 21 | static time_t GetGlobalCurrent(); 22 | public: 23 | PerformanceTimer() = default; 24 | virtual ~PerformanceTimer() override = default; 25 | virtual void Start() override; 26 | virtual void Pause() override; 27 | virtual void Resume() override; 28 | virtual ITimer* Update() override; 29 | virtual void Stop() override; 30 | virtual void AddOffset(int64_t offset) override; 31 | virtual time_t GetMilliseconds() override; 32 | private: 33 | LargeInteger mFrequency; 34 | LargeInteger mBeginCounter; 35 | int64_t mTimeBase = 0; 36 | int64_t mCurrent = 0; 37 | }; 38 | 39 | } 40 | 41 | #endif // _WTF_PERFORMANCE_TIMER_HPP 42 | -------------------------------------------------------------------------------- /src/R2LDanmaku.cpp: -------------------------------------------------------------------------------- 1 | #include "Displayer.hpp" 2 | #include "R2LDanmaku.hpp" 3 | #include "DanmakuConfig.hpp" 4 | #include "DanmakusRetainer.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | R2LDanmaku::~R2LDanmaku() { 9 | 10 | } 11 | 12 | DanmakuType R2LDanmaku::GetType() { 13 | return kScrolling; 14 | } 15 | 16 | void R2LDanmaku::Measure(Displayer* displayer, DanmakuConfig* config) { 17 | this->BaseDanmaku::Measure(displayer, config); 18 | float scaleFactor = displayer->GetDpiX() / 96.0f; 19 | mSpeed = (config->LogicalScreenWidth * scaleFactor + mTextWidth) / config->DanmakuDuration; 20 | int screenWidth = displayer->GetWidth(); 21 | mDuration = static_cast((screenWidth + mTextWidth) / mSpeed); 22 | } 23 | 24 | void R2LDanmaku::Layout(Displayer* displayer, DanmakuConfig* config, float x, float y) { 25 | this->y = y; 26 | mRect.top = y; 27 | mLayoutFlag = config->LayoutFlag; 28 | } 29 | 30 | bool R2LDanmaku::IsAlive(time_t time) { 31 | time_t elapsed = time - this->mStartTime; 32 | return elapsed > 0 && elapsed <= mDuration; 33 | } 34 | 35 | float R2LDanmaku::GetSpeed() { 36 | return mSpeed; 37 | } 38 | 39 | float R2LDanmaku::GetLeftAtTime(Displayer* displayer, time_t time) { 40 | time_t elapsedTime = time - this->mStartTime; 41 | 42 | return displayer->GetWidth() - elapsedTime * mSpeed; 43 | } 44 | 45 | Rect R2LDanmaku::GetRectAtTime(Displayer* displayer, time_t time) { 46 | mRect.left = GetLeftAtTime(displayer, time); 47 | mRect.right = mRect.left + mTextWidth; 48 | mRect.top = y; 49 | mRect.bottom = mRect.top + mTextHeight; 50 | 51 | return mRect; 52 | } 53 | 54 | class R2LDanmaku::R2LRetainer : public IDanmakusRetainer { 55 | public: 56 | virtual ~R2LRetainer() override = default; 57 | 58 | virtual void Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis) override { 59 | if (nullptr == mDanmakus) { 60 | mDanmakus = std::make_unique(); 61 | } 62 | 63 | RemoveTimeoutDanmakus(currentMillis); 64 | 65 | if (!danmaku->IsAlive(currentMillis)) { 66 | return; 67 | } 68 | 69 | float top = 0.0f; 70 | 71 | for (auto iter = mDanmakus->begin(); iter != mDanmakus->end(); ++iter) { 72 | DanmakuRef item = iter->second; 73 | Rect itemRect = item->GetRect(); 74 | 75 | if (item.Get() == danmaku.Get()) { 76 | return; 77 | } 78 | 79 | if (itemRect.top > top) { 80 | if (danmaku->GetHeight() <= itemRect.top - top) { 81 | break; 82 | } 83 | } 84 | 85 | bool suitable = false; 86 | float speedDiff = danmaku->GetSpeed() - item->GetSpeed(); 87 | 88 | if (danmaku->GetLeftAtTime(displayer, currentMillis) <= itemRect.right + 30) { 89 | suitable = false; 90 | } else if (speedDiff <= 0) { 91 | if (itemRect.right <= displayer->GetWidth() - 30) { 92 | suitable = true; 93 | } 94 | } else { 95 | time_t meetDuration = static_cast( 96 | (item->GetSpeed() * (currentMillis - item->GetStartTime()) - item->GetWidth()) / speedDiff 97 | ); 98 | if (meetDuration * danmaku->GetSpeed() >= displayer->GetWidth() + 30) { 99 | suitable = true; 100 | } 101 | } 102 | 103 | if (suitable) { 104 | top = itemRect.top; 105 | break; 106 | } 107 | 108 | top = itemRect.bottom + 1.0f; 109 | if (top + danmaku->GetHeight() > displayer->GetHeight()) { 110 | danmaku->SetSkipped(true); 111 | danmaku->Layout(displayer, config, static_cast(displayer->GetWidth()), -danmaku->GetHeight() - 1); 112 | return; 113 | } 114 | } 115 | 116 | danmaku->Layout(displayer, config, static_cast(displayer->GetWidth()), top); 117 | int topint = static_cast(top); 118 | auto iter = mDanmakus->find(topint); 119 | if (iter != mDanmakus->end()) { 120 | iter->second = danmaku; 121 | } else { 122 | mDanmakus->insert(std::make_pair(topint, danmaku)); 123 | } 124 | } 125 | 126 | void RemoveTimeoutDanmakus(time_t time) { 127 | auto iter = mDanmakus->begin(); 128 | while (iter != mDanmakus->end()) { 129 | if (iter->second->IsAlive(time) == false) { 130 | iter = mDanmakus->erase(iter); 131 | } else { 132 | ++iter; 133 | } 134 | } 135 | } 136 | 137 | virtual void Clear() override { 138 | mDanmakus->clear(); 139 | } 140 | 141 | virtual void Release() override { 142 | mDanmakus.reset(); 143 | } 144 | private: 145 | unique_ptr mDanmakus; 146 | }; 147 | 148 | std::unique_ptr R2LDanmaku::CreateRetainer() { 149 | return std::unique_ptr(new R2LRetainer); 150 | } 151 | 152 | } -------------------------------------------------------------------------------- /src/R2LDanmaku.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_R2L_DANMAKU_HPP 2 | #define _WTF_R2L_DANMAKU_HPP 3 | 4 | #include "Noncopyable.hpp" 5 | #include "BaseDanmaku.hpp" 6 | 7 | namespace WTFDanmaku { 8 | 9 | class IDanmakusRetainer; 10 | 11 | class R2LDanmaku : public BaseDanmaku { 12 | public: 13 | static inline DanmakuRef Create() { 14 | return xl::RefPtr(new R2LDanmaku); 15 | } 16 | static std::unique_ptr CreateRetainer(); 17 | public: 18 | explicit R2LDanmaku() = default; 19 | 20 | virtual ~R2LDanmaku() override; 21 | 22 | virtual DanmakuType GetType() override; 23 | 24 | virtual void Measure(Displayer* displayer, DanmakuConfig* config) override; 25 | 26 | virtual void Layout(Displayer* displayer, DanmakuConfig* config, float x, float y) override; 27 | 28 | virtual bool IsAlive(time_t time) override; 29 | 30 | virtual float GetSpeed() override; 31 | 32 | virtual float GetLeftAtTime(Displayer* displayer, time_t time) override; 33 | 34 | virtual Rect GetRectAtTime(Displayer* displayer, time_t time) override; 35 | private: 36 | class R2LRetainer; 37 | private: 38 | float y = 0.0f; 39 | float mSpeed = 0.0f; 40 | }; 41 | 42 | } 43 | 44 | #endif // _WTF_R2L_DANMAKU_HPP -------------------------------------------------------------------------------- /src/Rect.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_RECT_HPP 2 | #define _WTF_RECT_HPP 3 | 4 | namespace WTFDanmaku { 5 | 6 | template 7 | struct Rect { 8 | public: 9 | Rect() = default; 10 | 11 | Rect(T left, T top, T right, T bottom) { 12 | this->left = left; 13 | this->top = top; 14 | this->right = right; 15 | this->bottom = bottom; 16 | } 17 | public: 18 | T left = 0; 19 | T top = 0; 20 | T right = 0; 21 | T bottom = 0; 22 | }; 23 | 24 | } 25 | 26 | #endif // _WTF_RECT_HPP 27 | -------------------------------------------------------------------------------- /src/Renderable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "BaseDanmaku.hpp" 3 | #include "Displayer.hpp" 4 | #include "Renderable.hpp" 5 | #include "DanmakuConfig.hpp" 6 | #include "OutlineTextRenderer.hpp" 7 | 8 | namespace WTFDanmaku { 9 | 10 | bool Renderable::BuildTextLayout(Displayer* displayer, DanmakuConfig* config) { 11 | ComPtr dwFactory = displayer->GetDWriteFactory(); 12 | if (nullptr == dwFactory) 13 | return false; 14 | 15 | ComPtr textFormat = nullptr; 16 | 17 | float fontSize = mDanmaku->mTextSize * config->FontScaleFactor; 18 | fontSize *= displayer->GetDpiY() / 96.0f; 19 | 20 | HRESULT hr = dwFactory->CreateTextFormat(config->FontName.c_str(), nullptr, config->FontWeight, config->FontStyle, 21 | config->FontStretch, fontSize, L"zh-cn", &textFormat); 22 | if (FAILED(hr) || nullptr == textFormat) 23 | return false; 24 | 25 | textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); 26 | textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); 27 | 28 | hr = dwFactory->CreateTextLayout(mDanmaku->mComment.c_str(), static_cast(mDanmaku->mComment.length()), textFormat.Get(), 8192.0f, 2160.0f, &mTextLayout); 29 | if (FAILED(hr) || nullptr == mTextLayout) 30 | return false; 31 | 32 | DWRITE_TEXT_METRICS metrics = {0}; 33 | hr = mTextLayout->GetMetrics(&metrics); 34 | if (FAILED(hr)) 35 | return false; 36 | 37 | mDanmaku->mTextWidth = std::ceilf(metrics.width); 38 | mDanmaku->mTextHeight = std::ceilf(metrics.height); 39 | 40 | return true; 41 | } 42 | 43 | bool Renderable::HasBitmap(DanmakuConfig* config) { 44 | return mBitmap.Get() != nullptr 45 | && mBitmapValidFlag == config->BitmapValidFlag; 46 | } 47 | 48 | bool Renderable::BuildBitmap(Displayer* displayer, DanmakuConfig* config) { 49 | if (!HasTextLayout()) 50 | return false; 51 | 52 | ComPtr d2dFactory = displayer->GetD2DFactory(); 53 | if (nullptr == d2dFactory) 54 | return false; 55 | 56 | ComPtr bmp = displayer->CreateBitmap( 57 | static_cast(mDanmaku->mTextWidth), 58 | static_cast(mDanmaku->mTextHeight) 59 | ); 60 | if (bmp == nullptr) 61 | return false; 62 | 63 | float strokeWidth = 1.5f * config->FontScaleFactor; 64 | strokeWidth *= displayer->GetDpiY() / 96.0f; 65 | 66 | ComPtr deviceContext = displayer->AcquireDeviceContext(bmp); 67 | if (deviceContext == nullptr) 68 | return false; 69 | 70 | deviceContext->BeginDraw(); 71 | 72 | deviceContext->Clear(); 73 | deviceContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); 74 | 75 | ComPtr brush; 76 | deviceContext->CreateSolidColorBrush(D2D1::ColorF(mDanmaku->mTextColor), &brush); 77 | 78 | ComPtr outlineBrush; 79 | deviceContext->CreateSolidColorBrush(D2D1::ColorF(mDanmaku->mTextShadowColor, 1.0f), &outlineBrush); 80 | 81 | switch (config->DanmakuStyle) { 82 | case kOutline: { 83 | ComPtr textRenderer(new OutlineTextRenderer(d2dFactory, deviceContext, outlineBrush, strokeWidth, brush)); 84 | mTextLayout->Draw(deviceContext.Get(), textRenderer.Get(), 0.0f, 0.0f); 85 | break; 86 | } 87 | case kProjection: { 88 | deviceContext->DrawTextLayout(D2D1::Point2F(1.0f, 1.0f), mTextLayout.Get(), outlineBrush.Get()); 89 | deviceContext->DrawTextLayout(D2D1::Point2F(0.0f, 0.0f), mTextLayout.Get(), brush.Get()); 90 | break; 91 | } 92 | } 93 | 94 | HRESULT hr = deviceContext->EndDraw(); 95 | 96 | displayer->ReleaseDeviceContext(deviceContext); 97 | 98 | if (FAILED(hr)) 99 | return false; 100 | 101 | mBitmap = bmp; 102 | mBitmapValidFlag = config->BitmapValidFlag; 103 | return true; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/Renderable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_RENDERABLE_HPP 2 | #define _WTF_RENDERABLE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Noncopyable.hpp" 9 | 10 | using Microsoft::WRL::ComPtr; 11 | 12 | namespace WTFDanmaku { 13 | 14 | class Displayer; 15 | class BaseDanmaku; 16 | struct DanmakuConfig; 17 | 18 | class Renderable : public Noncopyable { 19 | public: 20 | explicit Renderable(BaseDanmaku* danmaku) : mDanmaku(danmaku) { } 21 | 22 | ~Renderable() = default; 23 | 24 | inline bool HasTextLayout() { 25 | return mTextLayout.Get() != nullptr; 26 | } 27 | 28 | inline ComPtr GetTextLayout() { 29 | return mTextLayout; 30 | } 31 | 32 | bool BuildTextLayout(Displayer* displayer, DanmakuConfig* config); 33 | 34 | bool HasBitmap(DanmakuConfig* config); 35 | 36 | inline ComPtr GetBitmap() { 37 | return mBitmap; 38 | } 39 | 40 | bool BuildBitmap(Displayer* displayer, DanmakuConfig* config); 41 | 42 | inline void Release() { 43 | mTextLayout.Reset(); 44 | mBitmap.Reset(); 45 | } 46 | private: 47 | BaseDanmaku* mDanmaku = nullptr; 48 | ComPtr mTextLayout = nullptr; 49 | ComPtr mBitmap = nullptr; 50 | int mBitmapValidFlag = -1; 51 | }; 52 | 53 | } 54 | 55 | #endif // _WTF_RENDERABLE_HPP -------------------------------------------------------------------------------- /src/StringUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "StringUtils.hpp" 2 | #include 3 | 4 | namespace WTFDanmaku { 5 | 6 | std::wstring UTF8ToWideString(const char* input) { 7 | int length = MultiByteToWideChar(CP_UTF8, 0, input, -1, nullptr, 0); 8 | std::wstring result; 9 | if (length > 0) { 10 | result.resize(length); 11 | MultiByteToWideChar(CP_UTF8, 0, input, -1, const_cast(result.c_str()), length); 12 | } 13 | return result; 14 | } 15 | 16 | void SplitString(const char* input, char delimiter, std::vector& output) { 17 | while (auto next = strchr(input, delimiter)) { 18 | output.push_back(std::string(input, next)); 19 | input = next + 1; 20 | } 21 | output.push_back(std::string(input)); 22 | } 23 | 24 | void ReplaceStringInplace(std::wstring& str, const wchar_t* search, const wchar_t* replace) { 25 | size_t search_length = wcslen(search); 26 | size_t replace_length = wcslen(replace); 27 | 28 | size_t pos = 0; 29 | while ((pos = str.find(search, pos)) != std::wstring::npos) { 30 | str.replace(pos, search_length, replace); 31 | pos += replace_length; 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/StringUtils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_STRING_UTILS_HPP 2 | #define _WTF_STRING_UTILS_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace WTFDanmaku { 8 | 9 | std::wstring UTF8ToWideString(const char* input); 10 | void SplitString(const char* input, char delimiter, std::vector& output); 11 | void ReplaceStringInplace(std::wstring& str, const wchar_t* search, const wchar_t* replace); 12 | 13 | } 14 | 15 | #endif // _WTF_STRING_UTILS_HPP -------------------------------------------------------------------------------- /src/TopDanmaku.cpp: -------------------------------------------------------------------------------- 1 | #include "Displayer.hpp" 2 | #include "TopDanmaku.hpp" 3 | #include "DanmakuConfig.hpp" 4 | #include "DanmakusRetainer.hpp" 5 | 6 | namespace WTFDanmaku { 7 | 8 | TopDanmaku::~TopDanmaku() { 9 | 10 | } 11 | 12 | DanmakuType TopDanmaku::GetType() { 13 | return kTop; 14 | } 15 | 16 | void TopDanmaku::Layout(Displayer* displayer, DanmakuConfig* config, float x, float y) { 17 | this->y = y; 18 | mRect.top = y; 19 | mLayoutFlag = config->LayoutFlag; 20 | } 21 | 22 | float TopDanmaku::GetSpeed() { 23 | return 0.0f; 24 | } 25 | 26 | float TopDanmaku::GetLeftAtTime(Displayer* displayer, time_t time) { 27 | float screenWidth = static_cast(displayer->GetWidth()); 28 | 29 | return screenWidth / 2 - mTextWidth / 2; 30 | } 31 | 32 | Rect TopDanmaku::GetRectAtTime(Displayer* displayer, time_t time) { 33 | mRect.left = GetLeftAtTime(displayer, time); 34 | mRect.right = mRect.left + mTextWidth; 35 | mRect.top = y; 36 | mRect.bottom = mRect.top + mTextHeight; 37 | 38 | return mRect; 39 | } 40 | 41 | class TopDanmaku::TopRetainer : public IDanmakusRetainer { 42 | public: 43 | virtual ~TopRetainer() override = default; 44 | 45 | virtual void Add(DanmakuRef danmaku, Displayer* displayer, DanmakuConfig* config, time_t currentMillis) override { 46 | if (nullptr == mDanmakus) { 47 | mDanmakus = std::make_unique(); 48 | } 49 | 50 | RemoveTimeoutDanmakus(currentMillis); 51 | 52 | if (!danmaku->IsAlive(currentMillis)) { 53 | return; 54 | } 55 | 56 | float top = 0.0f; 57 | 58 | for (auto iter = mDanmakus->begin(); iter != mDanmakus->end(); ++iter) { 59 | DanmakuRef item = iter->second; 60 | Rect itemRect = item->GetRect(); 61 | 62 | if (item.Get() == danmaku.Get()) { 63 | return; 64 | } 65 | 66 | if (itemRect.top > top) { 67 | if (danmaku->GetHeight() <= itemRect.top - top) { 68 | break; 69 | } 70 | } 71 | 72 | top = itemRect.bottom + 1.0f; 73 | if (top + danmaku->GetHeight() > displayer->GetHeight()) { 74 | top = 0.0f; 75 | break; 76 | } 77 | } 78 | 79 | danmaku->Layout(displayer, config, 0.0f, top); 80 | int topint = static_cast(top); 81 | auto iter = mDanmakus->find(topint); 82 | if (iter != mDanmakus->end()) { 83 | iter->second = danmaku; 84 | } else { 85 | mDanmakus->insert(std::make_pair(topint, danmaku)); 86 | } 87 | } 88 | 89 | void RemoveTimeoutDanmakus(time_t time) { 90 | auto iter = mDanmakus->begin(); 91 | while (iter != mDanmakus->end()) { 92 | if (iter->second->IsAlive(time) == false) { 93 | iter = mDanmakus->erase(iter); 94 | } else { 95 | ++iter; 96 | } 97 | } 98 | } 99 | 100 | virtual void Clear() override { 101 | mDanmakus->clear(); 102 | } 103 | 104 | virtual void Release() override { 105 | mDanmakus.reset(); 106 | } 107 | private: 108 | unique_ptr mDanmakus; 109 | }; 110 | 111 | std::unique_ptr TopDanmaku::CreateRetainer() { 112 | return std::unique_ptr(new TopRetainer); 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /src/TopDanmaku.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_TOP_DANMAKU_HPP 2 | #define _WTF_TOP_DANMAKU_HPP 3 | 4 | #include "Noncopyable.hpp" 5 | #include "BaseDanmaku.hpp" 6 | 7 | namespace WTFDanmaku { 8 | 9 | class IDanmakusRetainer; 10 | 11 | class TopDanmaku : public BaseDanmaku { 12 | public: 13 | static inline DanmakuRef Create() { 14 | return xl::RefPtr(new TopDanmaku); 15 | } 16 | static std::unique_ptr CreateRetainer(); 17 | public: 18 | explicit TopDanmaku() = default; 19 | 20 | virtual ~TopDanmaku() override; 21 | 22 | virtual DanmakuType GetType() override; 23 | 24 | virtual void Layout(Displayer* displayer, DanmakuConfig* config, float x, float y) override; 25 | 26 | virtual float GetSpeed() override; 27 | 28 | virtual float GetLeftAtTime(Displayer* displayer, time_t time) override; 29 | 30 | virtual Rect GetRectAtTime(Displayer* displayer, time_t time) override; 31 | private: 32 | class TopRetainer; 33 | private: 34 | float y = 0.0f; 35 | }; 36 | 37 | } 38 | 39 | #endif // _WTF_TOP_DANMAKU_HPP -------------------------------------------------------------------------------- /src/WTFDanmaku.cpp: -------------------------------------------------------------------------------- 1 | #include "Controller.hpp" 2 | #include "BaseDanmaku.hpp" 3 | #include "DanmakusManager.hpp" 4 | #include "DanmakuConfig.hpp" 5 | #include "DanmakuFactory.hpp" 6 | #include "BilibiliParser.hpp" 7 | #include "../include/WTFWindow.hpp" 8 | #include "../include/WTFDanmaku.h" 9 | 10 | using namespace WTFDanmaku; 11 | 12 | WTF_C_API WTF_Instance* __stdcall WTF_CreateInstance() { 13 | WTF_Instance* instance = new WTF_Instance; 14 | instance->controller = new Controller; 15 | return instance; 16 | } 17 | 18 | WTF_C_API void __stdcall WTF_ReleaseInstance(WTF_Instance* instance) { 19 | Controller* controller = reinterpret_cast(instance->controller); 20 | 21 | Controller::State state = controller->GetState(); 22 | if (state == Controller::State::kRunning || state == Controller::State::kPaused) { 23 | controller->Stop(); 24 | } 25 | delete controller; 26 | delete instance; 27 | } 28 | 29 | WTF_C_API int __stdcall WTF_InitializeWithHwnd(WTF_Instance* instance, void* hwnd) { 30 | Controller* controller = reinterpret_cast(instance->controller); 31 | 32 | return controller->Initialize(hwnd); 33 | } 34 | 35 | WTF_C_API int __stdcall WTF_InitializeOffscreen(WTF_Instance* instance, uint32_t initialWidth, uint32_t initialHeight) { 36 | Controller* controller = reinterpret_cast(instance->controller); 37 | 38 | return controller->Initialize(NULL, initialWidth, initialHeight); 39 | } 40 | 41 | WTF_C_API void __stdcall WTF_Terminate(WTF_Instance* instance) { 42 | Controller* controller = reinterpret_cast(instance->controller); 43 | 44 | controller->Terminate(); 45 | } 46 | 47 | WTF_C_API int __stdcall WTF_QuerySwapChain(WTF_Instance* instance, const void* pGuid, void** ppObject) { 48 | Controller* controller = reinterpret_cast(instance->controller); 49 | 50 | return controller->QuerySwapChain(pGuid, ppObject); 51 | } 52 | 53 | WTF_C_API void __stdcall WTF_LoadBilibiliFile(WTF_Instance* instance, const char* filePath) { 54 | Controller* controller = reinterpret_cast(instance->controller); 55 | 56 | ParserRef parser = BilibiliParser::Create(); 57 | parser->ParseFileSource(filePath); 58 | controller->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 59 | } 60 | 61 | WTF_C_API void __stdcall WTF_LoadBilibiliFileW(WTF_Instance* instance, const wchar_t* filePath) { 62 | Controller* controller = reinterpret_cast(instance->controller); 63 | 64 | ParserRef parser = BilibiliParser::Create(); 65 | parser->ParseFileSource(filePath); 66 | controller->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 67 | } 68 | 69 | WTF_C_API void __stdcall WTF_LoadBilibiliXml(WTF_Instance* instance, const char* str) { 70 | Controller* controller = reinterpret_cast(instance->controller); 71 | 72 | ParserRef parser = BilibiliParser::Create(); 73 | parser->ParseStringSource(str); 74 | controller->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 75 | } 76 | 77 | WTF_C_API void __stdcall WTF_AddDanmaku(WTF_Instance* instance, int type, int64_t time, const wchar_t* comment, int fontSize, int fontColor, int64_t timestamp, int danmakuId) { 78 | Controller* controller = reinterpret_cast(instance->controller); 79 | 80 | std::wstring cmt = comment; 81 | DanmakuRef danmaku = DanmakuFactory::CreateDanmaku(static_cast(type), time, cmt, fontSize, fontColor, timestamp, danmakuId); 82 | controller->GetManager()->AddDanmaku(danmaku); 83 | } 84 | 85 | WTF_C_API void __stdcall WTF_AddLiveDanmaku(WTF_Instance* instance, int type, int64_t time, const wchar_t* comment, int fontSize, int fontColor, int64_t timestamp, int danmakuId) { 86 | Controller* controller = reinterpret_cast(instance->controller); 87 | 88 | std::wstring cmt = comment; 89 | DanmakuRef danmaku = DanmakuFactory::CreateDanmaku(static_cast(type), time, cmt, fontSize, fontColor, timestamp, danmakuId); 90 | controller->GetManager()->AddLiveDanmaku(danmaku); 91 | } 92 | 93 | WTF_C_API void __stdcall WTF_Start(WTF_Instance* instance) { 94 | Controller* controller = reinterpret_cast(instance->controller); 95 | 96 | controller->Start(); 97 | } 98 | 99 | WTF_C_API void __stdcall WTF_Pause(WTF_Instance* instance) { 100 | Controller* controller = reinterpret_cast(instance->controller); 101 | 102 | controller->Pause(); 103 | } 104 | 105 | WTF_C_API void __stdcall WTF_Resume(WTF_Instance* instance) { 106 | Controller* controller = reinterpret_cast(instance->controller); 107 | 108 | controller->Resume(); 109 | } 110 | 111 | WTF_C_API void __stdcall WTF_Stop(WTF_Instance* instance) { 112 | Controller* controller = reinterpret_cast(instance->controller); 113 | 114 | controller->Stop(); 115 | } 116 | 117 | WTF_C_API void __stdcall WTF_SeekTo(WTF_Instance* instance, int64_t milliseconds) { 118 | Controller* controller = reinterpret_cast(instance->controller); 119 | 120 | controller->SeekTo(milliseconds); 121 | } 122 | 123 | WTF_C_API void __stdcall WTF_Resize(WTF_Instance* instance, uint32_t width, uint32_t height) { 124 | Controller* controller = reinterpret_cast(instance->controller); 125 | 126 | controller->Resize(width, height); 127 | } 128 | 129 | WTF_C_API void __stdcall WTF_SetDpi(WTF_Instance* instance, uint32_t dpiX, uint32_t dpiY) { 130 | Controller* controller = reinterpret_cast(instance->controller); 131 | 132 | controller->SetDpi(dpiX, dpiY); 133 | } 134 | 135 | WTF_C_API int64_t __stdcall WTF_GetCurrentPosition(WTF_Instance* instance) { 136 | Controller* controller = reinterpret_cast(instance->controller); 137 | 138 | return controller->GetCurrentPosition(); 139 | } 140 | 141 | WTF_C_API int __stdcall WTF_IsRunning(WTF_Instance* instance) { 142 | Controller* controller = reinterpret_cast(instance->controller); 143 | 144 | return static_cast(controller->IsRunning()); 145 | } 146 | 147 | WTF_C_API float __stdcall WTF_GetFontScaleFactor(WTF_Instance* instance) { 148 | Controller* controller = reinterpret_cast(instance->controller); 149 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 150 | 151 | return config->FontScaleFactor; 152 | } 153 | 154 | WTF_C_API void __stdcall WTF_SetFontScaleFactor(WTF_Instance* instance, float factor) { 155 | Controller* controller = reinterpret_cast(instance->controller); 156 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 157 | 158 | config->FontScaleFactor = factor; 159 | controller->ReLayout(); 160 | } 161 | 162 | WTF_C_API void __stdcall WTF_SetFontName(WTF_Instance* instance, const wchar_t* fontName) { 163 | Controller* controller = reinterpret_cast(instance->controller); 164 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 165 | 166 | config->FontName = std::wstring(fontName); 167 | } 168 | 169 | WTF_C_API void __stdcall WTF_SetFontWeight(WTF_Instance* instance, int dwriteFontWeight) { 170 | Controller* controller = reinterpret_cast(instance->controller); 171 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 172 | 173 | config->FontWeight = static_cast(dwriteFontWeight); 174 | } 175 | 176 | WTF_C_API void __stdcall WTF_SetFontStyle(WTF_Instance* instance, int dwriteFontStyle) { 177 | Controller* controller = reinterpret_cast(instance->controller); 178 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 179 | 180 | config->FontStyle = static_cast(dwriteFontStyle); 181 | } 182 | 183 | WTF_C_API void __stdcall WTF_SetFontStretch(WTF_Instance* instance, int dwriteFontStretch) { 184 | Controller* controller = reinterpret_cast(instance->controller); 185 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 186 | 187 | config->FontStretch = static_cast(dwriteFontStretch); 188 | } 189 | 190 | WTF_C_API void __stdcall WTF_SetDanmakuStyle(WTF_Instance* instance, int style) { 191 | Controller* controller = reinterpret_cast(instance->controller); 192 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 193 | 194 | config->DanmakuStyle = static_cast(style); 195 | } 196 | 197 | WTF_C_API void __stdcall WTF_SetCompositionOpacity(WTF_Instance* instance, float opacity) { 198 | Controller* controller = reinterpret_cast(instance->controller); 199 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 200 | 201 | float value = opacity > 1.0f ? 1.0f : (opacity < 0.0f ? 0.0f : opacity); 202 | config->CompositionOpacity = value; 203 | } 204 | 205 | WTF_C_API void __stdcall WTF_SetDanmakuTypeVisibility(WTF_Instance* instance, int params) { 206 | Controller* controller = reinterpret_cast(instance->controller); 207 | DanmakuConfig* config = controller->GetManager()->GetConfig(); 208 | 209 | config->R2LVisible = (params & WTF_DANMAKU_TYPE_SCROLLING_VISIBLE) ? true : false; 210 | config->TopVisible = (params & WTF_DANMAKU_TYPE_TOP_VISIBLE) ? true : false; 211 | config->BottomVisible = (params & WTF_DANMAKU_TYPE_BOTTOM_VISIBLE) ? true : false; 212 | } 213 | 214 | #ifndef _WTF_BUILD_UWP 215 | 216 | WTF_C_API WTF_Window* __stdcall WTFWindow_Create(void* hInstance, int nCmdShow) { 217 | WTFWindow* wtfwindow = new WTFWindow(static_cast(hInstance), nCmdShow); 218 | return reinterpret_cast(wtfwindow); 219 | } 220 | 221 | WTF_C_API void __stdcall WTFWindow_Release(WTF_Window* window) { 222 | WTFWindow* wtfwindow = reinterpret_cast(window); 223 | 224 | delete wtfwindow; 225 | } 226 | 227 | WTF_C_API void __stdcall WTFWindow_Initialize(WTF_Window* window, uint32_t dwExStyle, int width, int height, const wchar_t* title) { 228 | WTFWindow* wtfwindow = reinterpret_cast(window); 229 | 230 | wtfwindow->Initialize(dwExStyle, width, height, title); 231 | } 232 | 233 | WTF_C_API void* __stdcall WTFWindow_GetHwnd(WTF_Window* window) { 234 | WTFWindow* wtfwindow = reinterpret_cast(window); 235 | 236 | return wtfwindow->GetHwnd(); 237 | } 238 | 239 | WTF_C_API void __stdcall WTFWindow_SetCustomWndProc(WTF_Window* window, void* proc) { 240 | WTFWindow* wtfwindow = reinterpret_cast(window); 241 | 242 | wtfwindow->SetCustomWindowProc(reinterpret_cast(proc)); 243 | } 244 | 245 | WTF_C_API LRESULT __stdcall WTFWindow_DefaultWindowProc(WTF_Window* window, void* hwnd, uint32_t message, WPARAM wParam, LPARAM lParam) { 246 | WTFWindow* wtfwindow = reinterpret_cast(window); 247 | 248 | return wtfwindow->DefaultWindowProc(static_cast(hwnd), message, wParam, lParam); 249 | } 250 | 251 | WTF_C_API void __stdcall WTFWindow_SetHitTestOverEnabled(WTF_Window* window, int enabled) { 252 | WTFWindow* wtfwindow = reinterpret_cast(window); 253 | 254 | bool bEnabled = enabled == 0 ? false : true; 255 | wtfwindow->SetHitTestOverEnabled(bEnabled); 256 | } 257 | 258 | WTF_C_API int __stdcall WTFWindow_RunMessageLoop(WTF_Window* window) { 259 | WTFWindow* wtfwindow = reinterpret_cast(window); 260 | 261 | return wtfwindow->Run(); 262 | } 263 | 264 | #endif // !_WTF_BUILD_UWP -------------------------------------------------------------------------------- /src/WTFEngine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Controller.hpp" 3 | #include "BaseDanmaku.hpp" 4 | #include "DanmakusManager.hpp" 5 | #include "DanmakuFactory.hpp" 6 | #include "BilibiliParser.hpp" 7 | #include "../include/WTFEngine.hpp" 8 | 9 | namespace WTFDanmaku { 10 | 11 | WTFEngine::WTFEngine() { 12 | mController = new Controller; 13 | } 14 | 15 | WTFEngine::~WTFEngine() { 16 | Controller::State state = mController->GetState(); 17 | if (state == Controller::State::kRunning || state == Controller::State::kPaused) { 18 | mController->Stop(); 19 | } 20 | delete mController; 21 | } 22 | 23 | int WTFEngine::Initialize(void* hwnd) { 24 | return mController->Initialize(hwnd); 25 | } 26 | 27 | int WTFEngine::InitializeOffscreen(uint32_t initialWidth, uint32_t initialHeight) { 28 | return mController->Initialize(NULL, initialWidth, initialHeight); 29 | } 30 | 31 | void WTFEngine::Terminate() { 32 | mController->Terminate(); 33 | } 34 | 35 | int WTFEngine::QuerySwapChain(const void* pGuid, void** ppObject) { 36 | return mController->QuerySwapChain(pGuid, ppObject); 37 | } 38 | 39 | void WTFEngine::LoadBilibiliFile(const char* filePath) { 40 | ParserRef parser = BilibiliParser::Create(); 41 | parser->ParseFileSource(filePath); 42 | mController->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 43 | } 44 | 45 | void WTFEngine::LoadBilibiliFile(const wchar_t* filePath) { 46 | ParserRef parser = BilibiliParser::Create(); 47 | parser->ParseFileSource(filePath); 48 | mController->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 49 | } 50 | 51 | void WTFEngine::LoadBilibiliXml(const char* str) { 52 | ParserRef parser = BilibiliParser::Create(); 53 | parser->ParseStringSource(str); 54 | mController->GetManager()->SetDanmakuList(std::move(parser->GetDanmakus())); 55 | } 56 | 57 | void WTFEngine::AddDanmaku(Type type, time_t time, const wchar_t* comment, int fontSize, int fontColor, time_t timestamp, int danmakuId) { 58 | std::wstring cmt = comment; 59 | DanmakuRef danmaku = DanmakuFactory::CreateDanmaku(static_cast(type), time, cmt, fontSize, fontColor, timestamp, danmakuId); 60 | mController->GetManager()->AddDanmaku(danmaku); 61 | } 62 | 63 | void WTFEngine::AddLiveDanmaku(Type type, time_t time, const wchar_t* comment, int fontSize, int fontColor, time_t timestamp, int danmakuId) { 64 | std::wstring cmt = comment; 65 | DanmakuRef danmaku = DanmakuFactory::CreateDanmaku(static_cast(type), time, cmt, fontSize, fontColor, timestamp, danmakuId); 66 | mController->GetManager()->AddLiveDanmaku(danmaku); 67 | } 68 | 69 | void WTFEngine::Start() { 70 | mController->Start(); 71 | } 72 | 73 | void WTFEngine::Pause() { 74 | mController->Pause(); 75 | } 76 | 77 | void WTFEngine::Resume() { 78 | mController->Resume(); 79 | } 80 | 81 | void WTFEngine::Stop() { 82 | mController->Stop(); 83 | } 84 | 85 | void WTFEngine::SeekTo(time_t milliseconds) { 86 | mController->SeekTo(milliseconds); 87 | } 88 | 89 | void WTFEngine::Resize(uint32_t width, uint32_t height) { 90 | mController->Resize(width, height); 91 | } 92 | 93 | void WTFEngine::SetDpi(uint32_t dpiX, uint32_t dpiY) { 94 | mController->SetDpi(dpiX, dpiY); 95 | } 96 | 97 | time_t WTFEngine::GetCurrentPosition() { 98 | return mController->GetCurrentPosition(); 99 | } 100 | 101 | bool WTFEngine::IsRunning() { 102 | return mController->IsRunning(); 103 | } 104 | 105 | float WTFEngine::GetFontScaleFactor() { 106 | return mController->GetManager()->GetConfig()->FontScaleFactor; 107 | } 108 | 109 | void WTFEngine::SetFontScaleFactor(float factor) { 110 | mController->GetManager()->GetConfig()->FontScaleFactor = factor; 111 | mController->ReLayout(); 112 | } 113 | 114 | void WTFEngine::SetFontName(const wchar_t* fontName) { 115 | mController->GetManager()->GetConfig()->FontName = std::wstring(fontName); 116 | } 117 | 118 | void WTFEngine::SetFontWeight(int dwriteFontWeight) { 119 | mController->GetManager()->GetConfig()->FontWeight = static_cast(dwriteFontWeight); 120 | } 121 | 122 | void WTFEngine::SetFontStyle(int dwriteFontStyle) { 123 | mController->GetManager()->GetConfig()->FontStyle = static_cast(dwriteFontStyle); 124 | } 125 | 126 | void WTFEngine::SetFontStretch(int dwriteFontStretch) { 127 | mController->GetManager()->GetConfig()->FontStretch = static_cast(dwriteFontStretch); 128 | } 129 | 130 | void WTFEngine::SetDanmakuStyle(Style style) { 131 | mController->GetManager()->GetConfig()->DanmakuStyle = static_cast(style); 132 | } 133 | 134 | void WTFEngine::SetCompositionOpacity(float opacity) { 135 | float value = opacity > 1.0f ? 1.0f : (opacity < 0.0f ? 0.0f : opacity); 136 | mController->GetManager()->GetConfig()->CompositionOpacity = value; 137 | } 138 | 139 | void WTFEngine::SetDanmakuTypeVisibility(int params) { 140 | DanmakuConfig* config = mController->GetManager()->GetConfig(); 141 | 142 | config->R2LVisible = (params & ScrollingVisible) ? true : false; 143 | config->TopVisible = (params & TopVisible) ? true : false; 144 | config->BottomVisible = (params & BottomVisible) ? true : false; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/WTFWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef _WTF_BUILD_UWP 3 | #include 4 | #endif 5 | #include "../include/WTFWindow.hpp" 6 | 7 | #ifndef _WTF_BUILD_UWP 8 | 9 | #pragma comment (lib, "Comctl32.lib") 10 | 11 | namespace WTFDanmaku { 12 | 13 | WTFWindow::WTFWindow(HINSTANCE hInst, int nCmdShow) : m_hInstance(hInst), m_nCmdShow(nCmdShow) { } 14 | 15 | WTFWindow::~WTFWindow() { 16 | 17 | } 18 | 19 | int WTFWindow::Initialize(DWORD dwExStyle, int width, int height, const wchar_t* title) { 20 | INITCOMMONCONTROLSEX iccex = { 0 }; 21 | iccex.dwSize = sizeof(iccex); 22 | iccex.dwICC = ICC_WIN95_CLASSES; 23 | InitCommonControlsEx(&iccex); 24 | 25 | RegisterWindowClass(); 26 | 27 | if (title == nullptr) { 28 | title = L"WTFDanmaku SampleWindow"; 29 | } 30 | m_hWindow = CreateWindowEx(dwExStyle, m_WindowClassName, title, WS_OVERLAPPEDWINDOW, 31 | CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, m_hInstance, this); 32 | 33 | if (!m_hWindow) { 34 | return FALSE; 35 | } 36 | 37 | ShowWindow(m_hWindow, m_nCmdShow); 38 | UpdateWindow(m_hWindow); 39 | 40 | return TRUE; 41 | } 42 | 43 | void WTFWindow::SetCustomWindowProc(WNDPROC proc) { 44 | m_CustomWndProc = proc; 45 | } 46 | 47 | ATOM WTFWindow::RegisterWindowClass() { 48 | WNDCLASSEX wcex; 49 | memset(&wcex, 0, sizeof(wcex)); 50 | 51 | wcex.cbSize = sizeof(wcex); 52 | 53 | wcex.style = CS_HREDRAW | CS_VREDRAW; 54 | if (m_CustomWndProc) { 55 | wcex.lpfnWndProc = m_CustomWndProc; 56 | } else { 57 | wcex.lpfnWndProc = &WTFWindow::WndProc; 58 | } 59 | wcex.cbClsExtra = 0; 60 | wcex.cbWndExtra = 0; 61 | wcex.hInstance = m_hInstance; 62 | wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 63 | wcex.hIcon = LoadIcon(m_hInstance, IDI_APPLICATION); 64 | wcex.hIconSm = LoadIcon(m_hInstance, IDI_APPLICATION); 65 | wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 66 | wcex.lpszMenuName = NULL; 67 | wcex.lpszClassName = m_WindowClassName; 68 | 69 | return RegisterClassEx(&wcex); 70 | } 71 | 72 | void WTFWindow::SetHitTestOverEnabled(bool enabled) { 73 | if (m_hWindow) { 74 | long currentLong = GetWindowLong(m_hWindow, GWL_EXSTYLE); 75 | if (enabled) { 76 | SetWindowLong(m_hWindow, GWL_EXSTYLE, currentLong | WS_EX_LAYERED | WS_EX_TRANSPARENT); 77 | } 78 | else { 79 | SetWindowLong(m_hWindow, GWL_EXSTYLE, currentLong & ~(WS_EX_LAYERED | WS_EX_TRANSPARENT)); 80 | } 81 | } 82 | } 83 | 84 | int WTFWindow::Run() { 85 | MSG msg = { 0 }; 86 | 87 | while (GetMessage(&msg, NULL, 0, 0)) { 88 | TranslateMessage(&msg); 89 | DispatchMessage(&msg); 90 | } 91 | 92 | return static_cast(msg.wParam); 93 | } 94 | 95 | HWND WTFWindow::GetHwnd() { 96 | return m_hWindow; 97 | } 98 | 99 | LRESULT WTFWindow::DefaultWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 100 | return WndProc(hWnd, message, wParam, lParam); 101 | } 102 | 103 | LRESULT CALLBACK WTFWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 104 | WTFWindow* window = nullptr; 105 | 106 | if (WM_NCCREATE == message) { 107 | LPCREATESTRUCT cs = reinterpret_cast(lParam); 108 | window = static_cast(cs->lpCreateParams); 109 | SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(window)); 110 | } else { 111 | window = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); 112 | } 113 | 114 | if (window == nullptr) 115 | return DefWindowProc(hWnd, message, wParam, lParam); 116 | 117 | switch (message) { 118 | case WM_CREATE: { 119 | window->m_hWindow = hWnd; 120 | break; 121 | } 122 | case WM_DESTROY: 123 | PostQuitMessage(0); 124 | break; 125 | default: 126 | return DefWindowProc(hWnd, message, wParam, lParam); 127 | } 128 | 129 | return 0; 130 | } 131 | 132 | } 133 | 134 | #endif // !_WTF_BUILD_UWP -------------------------------------------------------------------------------- /src/Win32Mutex.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_WIN32_MUTEX_HPP 2 | #define _WTF_WIN32_MUTEX_HPP 3 | 4 | #include 5 | #include "Noncopyable.hpp" 6 | 7 | namespace WTFDanmaku { 8 | 9 | class Win32Mutex : public Noncopyable { 10 | public: 11 | inline Win32Mutex() { 12 | InitializeCriticalSectionEx(&cs, 0, CRITICAL_SECTION_NO_DEBUG_INFO); 13 | } 14 | 15 | inline ~Win32Mutex() { 16 | DeleteCriticalSection(&cs); 17 | } 18 | 19 | inline void lock() { 20 | EnterCriticalSection(&cs); 21 | } 22 | 23 | inline void unlock() { 24 | LeaveCriticalSection(&cs); 25 | } 26 | private: 27 | CRITICAL_SECTION cs; 28 | }; 29 | 30 | } 31 | #endif // _WTF_WIN32_MUTEX_HPP -------------------------------------------------------------------------------- /src/WinmmTimer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "WinmmTimer.hpp" 4 | 5 | #ifndef _WTF_BUILD_UWP 6 | 7 | #pragma comment (lib, "winmm.lib") 8 | 9 | namespace WTFDanmaku { 10 | 11 | time_t WinmmTimer::GetGlobalCurrent() { 12 | return timeGetTime(); 13 | } 14 | 15 | WinmmTimer::WinmmTimer() { 16 | timeBeginPeriod(1); 17 | } 18 | 19 | WinmmTimer::~WinmmTimer() { 20 | timeEndPeriod(1); 21 | } 22 | 23 | void WinmmTimer::Start() { 24 | mTimeBase = 0; 25 | mBeginTime = timeGetTime(); 26 | } 27 | 28 | void WinmmTimer::Pause() { 29 | mTimeBase += timeGetTime() - mBeginTime; 30 | } 31 | 32 | void WinmmTimer::Resume() { 33 | mBeginTime = timeGetTime(); 34 | } 35 | 36 | ITimer* WinmmTimer::Update() { 37 | mCurrent = timeGetTime() - mBeginTime + mTimeBase; 38 | return this; 39 | } 40 | 41 | void WinmmTimer::Stop() { 42 | mBeginTime = 0; 43 | mTimeBase = 0; 44 | } 45 | 46 | void WinmmTimer::AddOffset(int64_t offset) { 47 | mTimeBase += offset; 48 | } 49 | 50 | time_t WinmmTimer::GetMilliseconds() { 51 | return mCurrent; 52 | } 53 | 54 | } 55 | 56 | #endif // _WTF_BUILD_UWP -------------------------------------------------------------------------------- /src/WinmmTimer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WTF_WINMM_TIMER_HPP 2 | #define _WTF_WINMM_TIMER_HPP 3 | 4 | #include "ITimer.hpp" 5 | 6 | #ifndef _WTF_BUILD_UWP 7 | 8 | namespace WTFDanmaku { 9 | 10 | class WinmmTimer : public ITimer { 11 | public: 12 | static inline TimerRef Create() { 13 | return std::make_shared(); 14 | } 15 | static time_t GetGlobalCurrent(); 16 | public: 17 | WinmmTimer(); 18 | virtual ~WinmmTimer() override; 19 | virtual void Start() override; 20 | virtual void Pause() override; 21 | virtual void Resume() override; 22 | virtual ITimer* Update() override; 23 | virtual void Stop() override; 24 | virtual void AddOffset(int64_t offset) override; 25 | virtual time_t GetMilliseconds() override; 26 | private: 27 | int64_t mBeginTime = 0; 28 | int64_t mTimeBase = 0; 29 | int64_t mCurrent = 0; 30 | }; 31 | 32 | } 33 | 34 | #endif // _WTF_BUILD_UWP 35 | 36 | #endif // _WTF_WINMM_TIMER_HPP -------------------------------------------------------------------------------- /src/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { 4 | switch (ul_reason_for_call) { 5 | case DLL_PROCESS_ATTACH: 6 | case DLL_THREAD_ATTACH: 7 | case DLL_THREAD_DETACH: 8 | case DLL_PROCESS_DETACH: 9 | break; 10 | } 11 | return TRUE; 12 | } 13 | -------------------------------------------------------------------------------- /src/libwtfdanmaku.Shared.vcxitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | {6b39ffa8-cd29-4e39-8bb2-8c6b1f6ef2f9} 7 | libwtfdanmaku 8 | libwtfdanmaku.Shared 9 | 248F659F-DAC5-46E8-AC09-60EC9FC95053 10 | 11 | 12 | 13 | %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/libwtfdanmaku.Shared.vcxitems.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {6f645e0e-b34e-4077-9cb2-60793500fe14} 10 | 11 | 12 | {3945542b-fe74-4caa-b5c1-efb72f8657e3} 13 | 14 | 15 | 16 | 17 | src 18 | 19 | 20 | src 21 | 22 | 23 | src 24 | 25 | 26 | src 27 | 28 | 29 | src 30 | 31 | 32 | src 33 | 34 | 35 | src 36 | 37 | 38 | src 39 | 40 | 41 | src 42 | 43 | 44 | src 45 | 46 | 47 | src 48 | 49 | 50 | src 51 | 52 | 53 | src 54 | 55 | 56 | src 57 | 58 | 59 | src 60 | 61 | 62 | src 63 | 64 | 65 | src 66 | 67 | 68 | include 69 | 70 | 71 | include 72 | 73 | 74 | include 75 | 76 | 77 | include 78 | 79 | 80 | src 81 | 82 | 83 | src 84 | 85 | 86 | src\base 87 | 88 | 89 | src\base 90 | 91 | 92 | src\base 93 | 94 | 95 | src\base 96 | 97 | 98 | src\base 99 | 100 | 101 | src\base 102 | 103 | 104 | src\base 105 | 106 | 107 | src\base 108 | 109 | 110 | src 111 | 112 | 113 | src 114 | 115 | 116 | src 117 | 118 | 119 | 120 | 121 | src 122 | 123 | 124 | src 125 | 126 | 127 | src 128 | 129 | 130 | src 131 | 132 | 133 | src 134 | 135 | 136 | src 137 | 138 | 139 | src 140 | 141 | 142 | src 143 | 144 | 145 | src 146 | 147 | 148 | src 149 | 150 | 151 | src 152 | 153 | 154 | src 155 | 156 | 157 | src 158 | 159 | 160 | src 161 | 162 | 163 | src 164 | 165 | 166 | src 167 | 168 | 169 | src 170 | 171 | 172 | src 173 | 174 | 175 | src 176 | 177 | 178 | src 179 | 180 | 181 | --------------------------------------------------------------------------------