Просмотр исходного кода

commit: clean & import Karaoke PoC

HOME 4 лет назад
Родитель
Сommit
5a8ddd5244

+ 0 - 16
DrasticTemplateEngine/Class1.cs

@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace DrasticTemplateEngine
-{
-    public class DteRunner
-    {
-        public DteRunner()
-        {
-            
-        }
-    }
-}

+ 0 - 54
DrasticTemplateEngine/DrasticTemplateEngine.csproj

@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>a043ad4d-3ef9-4b8c-b90b-64f3b44eaae6</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>DrasticTemplateEngine</RootNamespace>
-    <AssemblyName>DrasticTemplateEngine</AssemblyName>
-    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
-    <FileAlignment>512</FileAlignment>
-    <Deterministic>true</Deterministic>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System"/>
-    
-    <Reference Include="System.Core"/>
-    <Reference Include="System.Xml.Linq"/>
-    <Reference Include="System.Data.DataSetExtensions"/>
-    
-    
-    <Reference Include="Microsoft.CSharp"/>
-    
-    <Reference Include="System.Data"/>
-    
-    <Reference Include="System.Net.Http"/>
-    
-    <Reference Include="System.Xml"/>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="Class1.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-  </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- </Project>

+ 6 - 0
KaraokeRenderPoc/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
+    </startup>
+</configuration>

+ 56 - 0
KaraokeRenderPoc/Karaoke/KaraokeLyric.cs

@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace KaraokeRenderPoc.Karaoke
+{
+    internal class KaraokeLyric
+    {
+        private readonly List<Sentence> _sentences;
+        public IReadOnlyList<Sentence> Sentences => _sentences;
+
+        public int Length
+        {
+            get
+            {
+                var last = Sentences.Last().Words.Last();
+                return last.StartPosition + last.Duration;
+            }
+        }
+
+        public KaraokeLyric(IReadOnlyList<string> lines)
+        {
+            var sentences = new List<Sentence>();
+
+            int position = 0;
+
+            List<Word> words = null;
+            int sentenceStartPosition = 0;
+
+            foreach (var line in lines)
+            {
+                if (line == "=")
+                {
+                    if (words != null)
+                    {
+                        sentences.Add(new Sentence(sentenceStartPosition, words.ToArray()));
+                    }
+                    words = new List<Word>();
+                    sentenceStartPosition = position;
+                    continue;
+                }
+
+                var parts = line.Split('|');
+                var duration = int.Parse(parts[2]);
+                words.Add(new Word(parts[0], parts[1], position, duration));
+                position += duration;
+            }
+
+            _sentences = sentences.ToList();
+        }
+
+        public int GetSentenceIndex(int position)
+        {
+            return _sentences.FindLastIndex(p => p.StartPosition <= position);
+        }
+    }
+}

+ 18 - 0
KaraokeRenderPoc/Karaoke/Sentence.cs

@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace KaraokeRenderPoc.Karaoke
+{
+    [DebuggerDisplay("{StartPosition}|{Words}")]
+    internal class Sentence
+    {
+        public int StartPosition { get; }
+        public IReadOnlyList<Word> Words { get; }
+
+        public Sentence(int startPosition, IReadOnlyList<Word> words)
+        {
+            StartPosition = startPosition;
+            Words = words;
+        }
+    }
+}

+ 22 - 0
KaraokeRenderPoc/Karaoke/Word.cs

@@ -0,0 +1,22 @@
+using System.Diagnostics;
+
+namespace KaraokeRenderPoc.Karaoke
+{
+    [DebuggerDisplay("{Text,nq}|{Ruby,nq}|{Duration,nq}|{StartPosition,nq}")]
+    internal class Word
+    {
+        public string Text { get; }
+        public string Ruby { get; }
+        public int StartPosition { get; }
+        public int Duration { get; }
+        public int EndPosition => StartPosition + Duration;
+
+        public Word(string text, string ruby, int startPosition, int duration)
+        {
+            Text = text;
+            Ruby = ruby;
+            StartPosition = startPosition;
+            Duration = duration;
+        }
+    }
+}

+ 90 - 0
KaraokeRenderPoc/KaraokeRenderPoc.csproj

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>KaraokeRenderPoc</RootNamespace>
+    <AssemblyName>KaraokeRenderPoc</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Karaoke\KaraokeLyric.cs" />
+    <Compile Include="Karaoke\Sentence.cs" />
+    <Compile Include="Karaoke\Word.cs" />
+    <Compile Include="RenderForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="RenderForm.Designer.cs">
+      <DependentUpon>RenderForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+      <DesignTime>True</DesignTime>
+    </Compile>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Resources\BackgroundImage.jpg" />
+    <Content Include="Resources\Example.txt" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 22 - 0
KaraokeRenderPoc/Program.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace KaraokeRenderPoc
+{
+    static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new RenderForm());
+        }
+    }
+}

+ 3 - 3
DrasticTemplateEngine/Properties/AssemblyInfo.cs

@@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
 // 有关程序集的一般信息由以下
 // 控制。更改这些特性值可修改
 // 与程序集关联的信息。
-[assembly: AssemblyTitle("DrasticTemplateEngine")]
+[assembly: AssemblyTitle("KaraokeRenderPoc")]
 [assembly: AssemblyDescription("")]
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("DrasticTemplateEngine")]
+[assembly: AssemblyProduct("KaraokeRenderPoc")]
 [assembly: AssemblyCopyright("Copyright ©  2019")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
 [assembly: ComVisible(false)]
 
 // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
-[assembly: Guid("a043ad4d-3ef9-4b8c-b90b-64f3b44eaae6")]
+[assembly: Guid("0862c4a6-1aab-4ba1-9b51-ea22d534e2f8")]
 
 // 程序集的版本信息由下列四个值组成: 
 //

+ 116 - 0
KaraokeRenderPoc/Properties/Resources.Designer.cs

@@ -0,0 +1,116 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace KaraokeRenderPoc.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KaraokeRenderPoc.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性
+        ///   重写当前线程的 CurrentUICulture 属性。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap BackgroundImage {
+            get {
+                object obj = ResourceManager.GetObject("BackgroundImage", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找类似 =
+        ///大|だい|150
+        ///事|じ|150
+        ///な| |150
+        ///も| |150
+        ///の| |150
+        ///を| |150
+        ///い| |150
+        ///つ| |150
+        ///も| |150
+        ///=
+        ///私|わたし|150
+        ///は| |150
+        ///间|ま|150
+        ///违|ちが|150
+        ///え| |150
+        ///る| |150
+        ///の| |150
+        ///=
+        ///微|ほほ|150
+        ///笑|え|150
+        ///み| |150
+        ///に| |150
+        ///=
+        ///み| |150
+        ///ん| |150
+        ///な| |150
+        ///何|なに|150
+        ///か| |150
+        ///を| |150
+        ///隠|かく|150
+        ///し| |150
+        ///て| |150
+        ///る| |150
+        ///= 的本地化字符串。
+        /// </summary>
+        internal static string Example {
+            get {
+                return ResourceManager.GetString("Example", resourceCulture);
+            }
+        }
+    }
+}

+ 127 - 0
KaraokeRenderPoc/Properties/Resources.resx

@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="BackgroundImage" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\resources\backgroundimage.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Example" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\resources\example.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
+  </data>
+</root>

+ 30 - 0
KaraokeRenderPoc/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace KaraokeRenderPoc.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
KaraokeRenderPoc/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 112 - 0
KaraokeRenderPoc/RenderForm.Designer.cs

@@ -0,0 +1,112 @@
+namespace KaraokeRenderPoc
+{
+    partial class RenderForm
+    {
+        /// <summary>
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows 窗体设计器生成的代码
+
+        /// <summary>
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.InputTextBox = new System.Windows.Forms.TextBox();
+            this.ParseButton = new System.Windows.Forms.Button();
+            this.PositionTrackBar = new System.Windows.Forms.TrackBar();
+            this.RenderPictureBox = new System.Windows.Forms.PictureBox();
+            ((System.ComponentModel.ISupportInitialize)(this.PositionTrackBar)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.RenderPictureBox)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // InputTextBox
+            // 
+            this.InputTextBox.Dock = System.Windows.Forms.DockStyle.Left;
+            this.InputTextBox.Location = new System.Drawing.Point(0, 0);
+            this.InputTextBox.Margin = new System.Windows.Forms.Padding(2);
+            this.InputTextBox.Multiline = true;
+            this.InputTextBox.Name = "InputTextBox";
+            this.InputTextBox.Size = new System.Drawing.Size(119, 566);
+            this.InputTextBox.TabIndex = 0;
+            // 
+            // ParseButton
+            // 
+            this.ParseButton.Dock = System.Windows.Forms.DockStyle.Top;
+            this.ParseButton.Location = new System.Drawing.Point(119, 0);
+            this.ParseButton.Margin = new System.Windows.Forms.Padding(2);
+            this.ParseButton.Name = "ParseButton";
+            this.ParseButton.Size = new System.Drawing.Size(585, 37);
+            this.ParseButton.TabIndex = 1;
+            this.ParseButton.Text = "Parse";
+            this.ParseButton.UseVisualStyleBackColor = true;
+            this.ParseButton.Click += new System.EventHandler(this.ParseButton_Click);
+            // 
+            // PositionTrackBar
+            // 
+            this.PositionTrackBar.Dock = System.Windows.Forms.DockStyle.Top;
+            this.PositionTrackBar.Location = new System.Drawing.Point(119, 37);
+            this.PositionTrackBar.Margin = new System.Windows.Forms.Padding(2);
+            this.PositionTrackBar.Name = "PositionTrackBar";
+            this.PositionTrackBar.Size = new System.Drawing.Size(585, 45);
+            this.PositionTrackBar.TabIndex = 2;
+            this.PositionTrackBar.TickFrequency = 100;
+            this.PositionTrackBar.Scroll += new System.EventHandler(this.PositionTrackBar_Scroll);
+            // 
+            // RenderPictureBox
+            // 
+            this.RenderPictureBox.BackgroundImage = global::KaraokeRenderPoc.Properties.Resources.BackgroundImage;
+            this.RenderPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.RenderPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.RenderPictureBox.Location = new System.Drawing.Point(119, 82);
+            this.RenderPictureBox.Margin = new System.Windows.Forms.Padding(2);
+            this.RenderPictureBox.Name = "RenderPictureBox";
+            this.RenderPictureBox.Size = new System.Drawing.Size(585, 484);
+            this.RenderPictureBox.TabIndex = 3;
+            this.RenderPictureBox.TabStop = false;
+            this.RenderPictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.RenderPictureBox_Paint);
+            // 
+            // RenderForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(704, 566);
+            this.Controls.Add(this.RenderPictureBox);
+            this.Controls.Add(this.PositionTrackBar);
+            this.Controls.Add(this.ParseButton);
+            this.Controls.Add(this.InputTextBox);
+            this.Margin = new System.Windows.Forms.Padding(2);
+            this.Name = "RenderForm";
+            this.Text = "Karaoke Render POC";
+            ((System.ComponentModel.ISupportInitialize)(this.PositionTrackBar)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.RenderPictureBox)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.TextBox InputTextBox;
+        private System.Windows.Forms.Button ParseButton;
+        private System.Windows.Forms.TrackBar PositionTrackBar;
+        private System.Windows.Forms.PictureBox RenderPictureBox;
+    }
+}
+

+ 127 - 0
KaraokeRenderPoc/RenderForm.cs

@@ -0,0 +1,127 @@
+using KaraokeRenderPoc.Karaoke;
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Text;
+using System.Windows.Forms;
+
+namespace KaraokeRenderPoc
+{
+    public partial class RenderForm : Form
+    {
+        private KaraokeLyric _karaoke;
+
+        public RenderForm()
+        {
+            InitializeComponent();
+            InputTextBox.Text = Properties.Resources.Example;
+        }
+
+        private void ParseButton_Click(object sender, EventArgs e)
+        {
+            _karaoke = new KaraokeLyric(InputTextBox.Lines);
+            PositionTrackBar.Maximum = _karaoke.Length;
+        }
+
+        private void PositionTrackBar_Scroll(object sender, EventArgs e)
+        {
+            RenderPictureBox.Invalidate();
+        }
+
+        private void RenderPictureBox_Paint(object sender, PaintEventArgs e)
+        {
+            var g = e.Graphics;
+
+            g.CompositingMode = CompositingMode.SourceOver;
+            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+            g.SmoothingMode = SmoothingMode.HighQuality;
+            g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
+
+            g.PageUnit = GraphicsUnit.Pixel;
+
+            var top = 400;
+
+            var correctWidth = -25f;
+
+            var readyOutlineColor = Color.Black;
+            var readyFillColor = Color.White;
+
+            var singOutlineColor = Color.White;
+            var singFillColor = Color.Blue;
+
+            var timelinePosition = PositionTrackBar.Value;
+
+            if (_karaoke != null)
+            {
+                var index = _karaoke.GetSentenceIndex(timelinePosition);
+
+                var sentence = _karaoke.Sentences[index];
+
+                using var rubyFont = new Font("方正准圆_GBK", 20, FontStyle.Bold);
+                using var textFont = new Font("方正准圆_GBK", 40, FontStyle.Bold);
+
+                var left = 0f;
+
+                foreach (var word in sentence.Words)
+                {
+                    var rubySize = g.MeasureString(word.Ruby, rubyFont);//g.MeasureDisplayString(word.Ruby, rubyFont);//TextRenderer.MeasureText(g, word.Ruby, rubyFont); //
+                    var textSize = g.MeasureString(word.Text, textFont);//g.MeasureDisplayString(word.Text, textFont);//TextRenderer.MeasureText(g, word.Text, textFont); //
+
+                    var maxWidth = Math.Max(rubySize.Width, textSize.Width);
+
+                    //g.DrawRectangle(Pens.Red, left, top, rubySize.Width, rubyFont.Height);
+                    //g.DrawRectangle(Pens.Red, left, top + rubyFont.Height, textSize.Width, textFont.Height);
+
+                    if (word.StartPosition <= timelinePosition && word.EndPosition >= timelinePosition)
+                    {
+                        var percent = ((float)timelinePosition - word.StartPosition) / word.Duration;
+
+                        var singWidth = maxWidth * percent;
+                        var readyWidth = maxWidth - singWidth;
+
+                        g.SetClip(new RectangleF(left, top, singWidth, rubyFont.Height + textFont.Height));
+                        g.DrawKaraoke(word.Ruby, rubyFont, new RectangleF(left, top, maxWidth, rubyFont.Height), singOutlineColor, singFillColor);
+                        g.DrawKaraoke(word.Text, textFont, new RectangleF(left, top + rubyFont.Height, maxWidth, textFont.Height), singOutlineColor, singFillColor);
+
+                        g.SetClip(new RectangleF(left + singWidth, top, readyWidth, rubyFont.Height + textFont.Height));
+                        g.DrawKaraoke(word.Ruby, rubyFont, new RectangleF(left, top, maxWidth, rubyFont.Height), readyOutlineColor, readyFillColor);
+                        g.DrawKaraoke(word.Text, textFont, new RectangleF(left, top + rubyFont.Height, maxWidth, textFont.Height), readyOutlineColor, readyFillColor);
+
+                        g.ResetClip();
+                    }
+                    else if (word.EndPosition < timelinePosition)
+                    {
+                        g.DrawKaraoke(word.Ruby, rubyFont, new RectangleF(left, top, maxWidth, rubyFont.Height), singOutlineColor, singFillColor);
+                        g.DrawKaraoke(word.Text, textFont, new RectangleF(left, top + rubyFont.Height, maxWidth, textFont.Height), singOutlineColor, singFillColor);
+                    }
+                    else
+                    {
+                        g.DrawKaraoke(word.Ruby, rubyFont, new RectangleF(left, top, maxWidth, rubySize.Height), readyOutlineColor, readyFillColor);
+                        g.DrawKaraoke(word.Text, textFont, new RectangleF(left, top + rubyFont.Height, maxWidth, textSize.Height), readyOutlineColor, readyFillColor);
+                    }
+
+                    left += correctWidth + maxWidth;
+                }
+            }
+        }
+    }
+
+    internal static class ExtensionMethods
+    {
+        public static void DrawKaraoke(this Graphics g, string s, Font f, RectangleF rectangle, Color outline, Color fill, int outlineWidth = 1)
+        {
+            using var path = new GraphicsPath();
+            {
+                using var alignment = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Near };
+                path.AddString(s, f.FontFamily, (int)f.Style, f.Size, rectangle, alignment);
+            }
+
+            using var fillBrush = new SolidBrush(fill);
+            g.FillPath(fillBrush, path);
+
+            using var outlinePen = new Pen(outline, outlineWidth);
+            g.DrawPath(outlinePen, path);
+        }
+    }
+}

BIN
KaraokeRenderPoc/Resources/BackgroundImage.jpg


+ 35 - 0
KaraokeRenderPoc/Resources/Example.txt

@@ -0,0 +1,35 @@
+=
+大|だい|150
+事|じ|150
+な| |150
+も| |150
+の| |150
+を| |150
+い| |150
+つ| |150
+も| |150
+=
+私|わたし|150
+は| |150
+间|ま|150
+违|ちが|150
+え| |150
+る| |150
+の| |150
+=
+微|ほほ|150
+笑|え|150
+み| |150
+に| |150
+=
+み| |150
+ん| |150
+な| |150
+何|なに|150
+か| |150
+を| |150
+隠|かく|150
+し| |150
+て| |150
+る| |150
+=

+ 5 - 5
PoC.sln

@@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "@", "@", "{65C086E5-79F9-46
 		README.md = README.md
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrasticTemplateEngine", "DrasticTemplateEngine\DrasticTemplateEngine.csproj", "{A043AD4D-3EF9-4B8C-B90B-64F3B44EAAE6}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KaraokeRenderPoc", "KaraokeRenderPoc\KaraokeRenderPoc.csproj", "{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -34,10 +34,10 @@ Global
 		{0580C2E7-AA6E-451F-AEE0-9AF30B244A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{0580C2E7-AA6E-451F-AEE0-9AF30B244A75}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{0580C2E7-AA6E-451F-AEE0-9AF30B244A75}.Release|Any CPU.Build.0 = Release|Any CPU
-		{A043AD4D-3EF9-4B8C-B90B-64F3B44EAAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{A043AD4D-3EF9-4B8C-B90B-64F3B44EAAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{A043AD4D-3EF9-4B8C-B90B-64F3B44EAAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{A043AD4D-3EF9-4B8C-B90B-64F3B44EAAE6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0862C4A6-1AAB-4BA1-9B51-EA22D534E2F8}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE