/* * Character3.cs * Copyright (c) 2007-2009 kbinani * * This file is part of LipSync. * * LipSync is free software; you can redistribute it and/or * modify it under the terms of the BSD License. * * LipSync is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; using LipSync.Properties; namespace LipSync { /// /// キャラクタを取り扱うクラス。 /// 第3世代 /// [Serializable] public class Character3 : ICloneable, IDisposable { string m_name; CharacterType m_type; ImageEntry[] m_basic = new ImageEntry[9]; List m_another; Size m_size; [NonSerialized] Bitmap m_cache = null; [NonSerialized] int[] m_cache_draw; string m_author; string m_version; PluginConfig m_plugin_config; bool m_updated = false; // 第2世代のCharacterからアップデートされたものであることを表すフラグ bool m_is_build_in = false; /// /// 使用上の注意など /// [OptionalField] string m_lisence; #region public static field /// /// ビルトイン・キャラクタ。 /// public static readonly Character3 Miku = new Character3( "Miku", "さなり", "", new ImageEntry[] { new ImageEntry( "base", Resources.b_miku175_base, "base", true ), new ImageEntry( "a", Resources.b_miku175_a, "mouth", false ), new ImageEntry( "aa", Resources.b_miku175_aa, "mouth", false ), new ImageEntry( "i", Resources.b_miku175_i, "mouth", false ), new ImageEntry( "u", Resources.b_miku175_u, "mouth", false ), new ImageEntry( "e", Resources.b_miku175_e, "mouth", false ), new ImageEntry( "o", Resources.b_miku175_o, "mouth", false ), new ImageEntry( "xo", Resources.b_miku175_xo, "mouth", false ), new ImageEntry( "nn", Resources.b_miku175_nn, "mouth", false ) }, new ImageEntry[] { new ImageEntry( "目閉じ", Resources.b_miku175_eyeclose, "eye", false ), new ImageEntry( "低目にっこり", Resources.b_miku175_smile, "eye", false ), new ImageEntry( "目半目", Resources.b_miku175_eyethin, "eye", false ), new ImageEntry( "こなた", Resources.b_miku175_konata, "eye", false ), new ImageEntry( "><", Resources.b_miku175_kudo, "eye", false ), new ImageEntry( "哀左ウィンク", Resources.b_miku175_winkleft, "eye", false ), new ImageEntry( "右ウィンク", Resources.b_miku175_winkright, "eye", false ), new ImageEntry( "bee", Resources.b_miku175_bee, "mouth", false), new ImageEntry( "neko", Resources.b_miku175_neko, "mouth", false ) }, true ); public static readonly Character3 Rin = new Character3( "Rin", "さなり", "", new ImageEntry[] { new ImageEntry( "base", Resources.b_rin100_base, "base",true ), new ImageEntry( "a", Resources.b_rin100_a, "mouth", false ), new ImageEntry( "aa", Resources.b_rin100_aa, "mouth", false ), new ImageEntry( "i", Resources.b_rin100_i, "mouth", false ), new ImageEntry( "u", Resources.b_rin100_u, "mouth", false ), new ImageEntry( "e", Resources.b_rin100_e, "mouth", false ), new ImageEntry( "o", Resources.b_rin100_o, "mouth", false ), new ImageEntry( "xo", Resources.b_rin100_xo, "mouth", false ), new ImageEntry( "nn", Resources.b_rin100_nn, "mouth", false ) }, new ImageEntry[] { new ImageEntry( "目閉じ", Resources.b_rin100_eyeclose, "eye", false ), new ImageEntry( "低目にっこり", Resources.b_rin100_smile, "eye", false ), new ImageEntry( "目半目", Resources.b_rin100_eyethin, "eye", false ), new ImageEntry( "><", Resources.b_rin100_kudo, "eye", false ), new ImageEntry( "哀左ウィンク", Resources.b_rin100_winkleft, "eye", false ), new ImageEntry( "低右ウィンク", Resources.b_rin100_winkright, "eye" , false), new ImageEntry( "bee", Resources.b_rin100_bee, "mouth", false ), new ImageEntry( "neko", Resources.b_rin100_neko, "mouth", false ), new ImageEntry( "きしし", Resources.b_rin100_kisisi, "mouth", false ) }, true ); public static readonly Character3 Len = new Character3( "Len", "さなり", "", new ImageEntry[] { new ImageEntry( "base", Resources.b_len100_base, "本体", true ), new ImageEntry( "a", Resources.b_len100_a, "mouth", false ), new ImageEntry( "aa", Resources.b_len100_aa, "mouth", false ), new ImageEntry( "i", Resources.b_len100_i, "mouth", false ), new ImageEntry( "u", Resources.b_len100_u, "mouth", false ), new ImageEntry( "e", Resources.b_len100_e, "mouth", false ), new ImageEntry( "o", Resources.b_len100_o, "mouth", false ), new ImageEntry( "xo", Resources.b_len100_xo, "mouth", false ), new ImageEntry( "nn", Resources.b_len100_nn, "mouth", false ) }, new ImageEntry[] { new ImageEntry( "目閉じ", Resources.b_len100_eyeclose, "eye", false ), new ImageEntry( "低目にっこり", Resources.b_len100_smile, "eye", false ), new ImageEntry( "目半目", Resources.b_len100_eyethin, "eye", false ), new ImageEntry( "(`・ω・´)", Resources.b_len100_shakin, "eye", false ), new ImageEntry( "哀左ウィンク", Resources.b_len100_winkleft, "eye", false ), new ImageEntry( "中右ウィンク", Resources.b_len100_winkright, "eye", false ), new ImageEntry( "きしし", Resources.b_len100_kisisi, "mouth", false ) }, true ); #endregion [OnDeserialized] private void onDeserialized( StreamingContext sc ) { SortedList slist = new SortedList(); foreach ( ImageEntry ie in m_basic ) { slist.Add( ie.Z, ie.title ); } foreach ( ImageEntry ie in m_another ) { slist.Add( ie.Z, ie.title ); } for ( int i = 0; i < slist.Keys.Count; i++ ) { string title = slist[slist.Keys[i]]; bool found = false; for ( int j = 0; j < m_basic.Length; j++ ) { if ( m_basic[j].title == title ) { m_basic[j].Z = i; found = true; break; } } if ( !found ) { for ( int j = 0; j < m_another.Count; j++ ) { if ( m_another[j].title == title ) { m_another[j].Z = i; break; } } } } } public bool IsBuildIn { get { return m_is_build_in; } } public void Remove( string title ) { for ( int i = 0; i < m_another.Count; i++ ) { if ( m_another[i].title == title ) { m_another.RemoveAt( i ); break; } } } public void SetImage( Image img, int index ) { this[index].SetImage( img ); } public void SetImage( Image img, string title ) { this[title].SetImage( img ); } public PluginConfig PluginConfig { get { return m_plugin_config; } set { m_plugin_config = value; } } public int Count { get { return 9 + m_another.Count; } } public void Dispose() { m_basic = null; m_another.Clear(); if ( m_cache != null ) { m_cache.Dispose(); } } public void Add( ImageEntry item ) { ImageEntry adding = (ImageEntry)item.Clone(); adding.Z = this.Count; m_another.Add( adding ); } public string Version { get { return m_version; } set { m_version = value; } } public string Author { get { return m_author; } set { m_author = value; } } public Character3() { m_name = ""; m_type = CharacterType.def; m_basic = new ImageEntry[9]; m_basic[0] = new ImageEntry( "base", null, "base", true ); m_basic[1] = new ImageEntry( "a", null, "mouth", false ); m_basic[2] = new ImageEntry( "aa", null, "mouth", false ); m_basic[3] = new ImageEntry( "i", null, "mouth", false ); m_basic[4] = new ImageEntry( "u", null, "mouth", false ); m_basic[5] = new ImageEntry( "e", null, "mouth", false ); m_basic[6] = new ImageEntry( "o", null, "mouth", false ); m_basic[7] = new ImageEntry( "xo", null, "mouth", false ); m_basic[8] = new ImageEntry( "nn", null, "mouth", false ); for ( int i = 0; i < 9; i++ ) { m_basic[i].Z = i; } m_another = new List(); m_size = new Size(); m_author = ""; m_version = ""; } public void Write( Stream s ) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize( s, this ); } public string GetMD5() { System.Security.Cryptography.MD5CryptoServiceProvider mcsp = new System.Security.Cryptography.MD5CryptoServiceProvider(); using ( MemoryStream ms = new MemoryStream() ) { this.Write( ms ); byte[] dat = mcsp.ComputeHash( ms ); string res = ""; foreach ( byte b in dat ) { res += b.ToString( "x2" ); } return res; } return ""; } /// /// ファイルに保存する /// /// /// public void WriteXml( string path ) { string f = Path.GetFileName( path ); if ( f != "content.xml" ) { return; } int width = this.Width; int height = this.Height; string base_path = Path.GetDirectoryName( path ); string image_path = Path.Combine( base_path, "images" ); if ( !Directory.Exists( image_path ) ) { Directory.CreateDirectory( Path.Combine( base_path, "images" ) ); } using ( FileStream fs = new FileStream( Path.Combine( base_path, "content.xml" ), FileMode.Create ) ) { XmlSerializer xs = new XmlSerializer( typeof( Character3 ) ); xs.Serialize( fs, this ); } using ( FileStream fs = new FileStream( Path.Combine( base_path, "basic.xml" ), FileMode.Create ) ) { XmlSerializer xs = new XmlSerializer( typeof( ImageEntry[] ) ); xs.Serialize( fs, m_basic ); } using ( FileStream fs = new FileStream( Path.Combine( base_path, "another.xml" ), FileMode.Create ) ) { XmlSerializer xs = new XmlSerializer( typeof( List ) ); xs.Serialize( fs, m_another ); } int count = -1; foreach ( ImageEntry img in this ) { count++; if ( img.Image != null ) { string file = Path.Combine( Path.Combine( base_path, "images" ), img.Z + ".png" ); Bitmap temp = img.GetImage( width, height ); temp.Save( file ); } } } public Character3( PluginConfig plugin_config ) { m_name = plugin_config.ID; m_type = CharacterType.plugin; m_plugin_config = plugin_config.Clone(); m_updated = false; } public static Character3 Read( Stream s ) { BinaryFormatter bf = new BinaryFormatter(); Character3 res = null; try { res = (Character3)bf.Deserialize( s ); return res; } catch { return null; } } /// /// xmlファイルからのコンストラクタ /// public static Character3 FromXml( string path ) { #if DEBUG Common.DebugWriteLine( "Character3.ctor(string);" ); #endif Character3 res; string dir = Path.GetDirectoryName( path ); using ( FileStream fs = new FileStream( path, FileMode.Open ) ) { XmlSerializer xs = new XmlSerializer( typeof( Character3 ) ); res = (Character3)xs.Deserialize( fs ); } using ( FileStream fs = new FileStream( Path.Combine( dir, "basic.xml" ), FileMode.Open ) ) { XmlSerializer xs = new XmlSerializer( typeof( ImageEntry[] ) ); res.m_basic = (ImageEntry[])xs.Deserialize( fs ); } using ( FileStream fs = new FileStream( Path.Combine( dir, "another.xml" ), FileMode.Open ) ) { XmlSerializer xs = new XmlSerializer( typeof( List ) ); res.m_another = (List)xs.Deserialize( fs ); } //res.ZReorder(); for ( int i = 0; i < res.Count; i++ ) { int z = res[i].Z; string file = Path.Combine( Path.Combine( dir, "images" ), z + ".png" ); #if DEBUG Common.DebugWriteLine( "Character3.ctor(String); file=" + file ); #endif if ( File.Exists( file ) ) { res[i].SetImage( Common.ImageFromFile( file ) ); } } #if DEBUG Common.DebugWriteLine( "Character3.FromXml()" ); for ( int i = 0; i < res.Count; i++ ) { Common.DebugWriteLine( "i=" + i + "; title=" + res[i].title + "; (image==null)=" + (res[i].Image == null) ); } Common.DebugWriteLine( "m_size=" + res.m_size ); #endif return res; } public static Character3 FromFile( string path ) { Character3 res; using ( FileStream fs = new FileStream( path, FileMode.Open ) ) { BinaryFormatter bf = new BinaryFormatter(); res = (Character3)bf.Deserialize( fs ); } return res; } /// /// 第2世代目のCharacterからのコンバート /// /// /// public Character3( Character character ) { List basic = new List(); List another = new List(); string[] titles = new string[] { "base", "a", "aa", "i", "u", "e", "o", "xo", "nn" }; // zオーダーを更新しておく for ( int i = 0; i < character.Images.Count; i++ ) { character.Images[i].Z = i; } #if DEBUG string t1 = ""; for ( int i = 0; i < character.Images.Count; i++ ) { t1 += character.Images[i].ToString() + "\n"; } System.Windows.Forms.MessageBox.Show( t1 ); #endif foreach ( string title in titles ) { bool found = false; foreach ( ImageEntry img in character.Images ) { if ( img.title == title ) { ImageEntry cp = new ImageEntry( img.title, null, img.tag, img.IsDefault, img.Z ); cp.SetImage( img.GetImage() ); basic.Add( cp ); found = true; break; } } if ( !found ) { if ( title == "base" ) { basic.Add( new ImageEntry( title, null, "base", true ) ); } else { basic.Add( new ImageEntry( title, null, "mouth", false ) ); } } } // another foreach ( ImageEntry img in character.Images ) { bool is_basic = false; foreach ( string title in titles ) { if ( img.title == title ) { is_basic = true; break; } } if ( !is_basic ) { ImageEntry cp = new ImageEntry( img.title, null, img.tag, img.IsDefault, img.Z ); cp.SetImage( img.GetImage() ); another.Add( cp ); } } m_name = character.Name; m_basic = basic.ToArray(); m_another = new List( another.ToArray() ); m_size = character.m_size; m_type = CharacterType.def; m_author = ""; m_version = ""; m_updated = true; //ZReorder(); #if DEBUG string t = ""; for ( int i = 0; i < m_basic.Length; i++ ) { t += m_basic[i].ToString() + "\n"; } for ( int i = 0; i < m_another.Count; i++ ) { t += m_another[i].ToString() + "\n"; } System.Windows.Forms.MessageBox.Show( t ); #endif } [XmlIgnore] public Bitmap DefaultFace { get { List type = new List(); int count = -1; foreach ( ImageEntry img in this ) { count++; if ( img.IsDefault ) { type.Add( count ); } } return Face( type.ToArray() ); } } public IEnumerator GetEnumerator() { for ( int i = 0; i < m_basic.Length; i++ ) { yield return m_basic[i]; } for ( int i = 0; i < m_another.Count; i++ ) { yield return m_another[i]; } } public Bitmap Face( int[] targets ) { if ( Width <= 0 || Height <= 0 ) { return null; } // zオーダー順に描画する画像を並べ替える int[] zorder = new int[targets.Length]; for ( int i = 0; i < targets.Length; i++ ) { zorder[i] = this[targets[i]].Z; } bool c = true; while ( c ) { c = false; for ( int i = 0; i < targets.Length - 1; i++ ) { if ( zorder[i] > zorder[i + 1] ) { int b = targets[i]; targets[i] = targets[i + 1]; targets[i + 1] = b; b = zorder[i]; zorder[i] = zorder[i + 1]; zorder[i + 1] = b; c = true; } } if ( !c ) { break; } } // 前回描画したのと同じ描画要求であれば、キャッシュをそのまま返す if ( m_cache != null && m_cache_draw != null ) { if ( m_cache_draw.Length == targets.Length ) { bool match = true; for ( int i = 0; i < targets.Length; i++ ) { if ( m_cache_draw[i] != targets[i] ) { match = false; break; } } if ( match ) { return (Bitmap)m_cache.Clone(); } } } m_cache_draw = targets; Bitmap bmp = new Bitmap( Width, Height ); using ( Graphics g = Graphics.FromImage( bmp ) ) { for ( int i = 0; i < targets.Length; i++ ) { ImageEntry img = this[targets[i]]; if ( img != null ) { img.DrawTo( g ); } } } if ( m_cache != null ) { m_cache = null; } m_cache = (Bitmap)bmp.Clone(); return bmp; } public ImageEntry this[string title] { get { for ( int i = 0; i < m_basic.Length; i++ ) { if ( m_basic[i].title == title ) { return m_basic[i]; } } for ( int i = 0; i < m_another.Count; i++ ) { if ( m_another[i].title == title ) { return m_another[i]; } } return null; } /*set { for ( int i = 0; i < m_basic.Length; i++ ) { if ( m_basic[i].title == title ) { m_basic[i] = value; } } for ( int i = 0; i < m_another.Count; i++ ) { if ( m_another[i].title == title ) { m_another[i] = value; } } }*/ } public ImageEntry this[int zorder] { get { for ( int i = 0; i < m_basic.Length; i++ ) { if ( m_basic[i].Z == zorder ) { return m_basic[i]; } } for ( int i = 0; i < m_another.Count; i++ ) { if ( m_another[i].Z == zorder ) { return m_another[i]; } } return null; } set { for ( int i = 0; i < m_basic.Length; i++ ) { if ( m_basic[i].Z == zorder ) { m_basic[i] = value; m_basic[i].Z = zorder; return; } } for ( int i = 0; i < m_another.Count; i++ ) { if ( m_another[i].Z == zorder ) { m_another[i] = value; m_another[i].Z = zorder; return; } } } } public Size Size { get { if ( m_type == CharacterType.def ) { return m_size; } else { throw new NotImplementedException(); } } set { m_size = value; } } [XmlIgnore] public int Width { get { return m_size.Width; } } [XmlIgnore] public int Height { get { return m_size.Height; } } public object Clone() { Character3 res = new Character3(); res.m_name = m_name; res.m_author = m_author; res.m_version = m_version; res.m_type = m_type; if ( m_plugin_config != null ) { res.m_plugin_config = m_plugin_config.Clone(); } for ( int i = 0; i < 9; i++ ) { res.m_basic[i] = (ImageEntry)m_basic[i].Clone(); } res.m_another.Clear(); for ( int i = 0; i < m_another.Count; i++ ) { res.m_another.Add( (ImageEntry)m_another[i].Clone() ); } res.m_updated = m_updated; res.m_size = m_size; return res; } private Character3( string name, string author, string version, ImageEntry[] basic, ImageEntry[] another, bool is_build_in ) : this( name, author, version, basic, another ) { m_is_build_in = is_build_in; } public Character3( string name, string author, string version, ImageEntry[] basic, ImageEntry[] another ) { m_type = CharacterType.def; m_name = name; m_author = author; m_version = version; if ( basic.Length < 9 ) { throw new ArgumentException( "basic.Length < 9" ); } int z = -1; for ( int i = 0; i < 9; i++ ) { z++; m_basic[i] = (ImageEntry)basic[i].Clone(); m_basic[i].Z = z; } if ( another != null ) { m_another = new List( another ); } else { m_another = new List(); } for ( int i = 0; i < m_another.Count; i++ ) { z++; m_another[i].Z = z; } int width = 0; int height = 0; if ( basic != null ) { foreach ( ImageEntry img in basic ) { if ( img.Image != null ) { width = Math.Max( width, img.Image.Width ); height = Math.Max( height, img.Image.Height ); } } } if ( another != null ) { foreach ( ImageEntry img in another ) { if ( img.Image != null ) { width = Math.Max( width, img.Image.Width ); height = Math.Max( height, img.Image.Height ); } } } //ZReorder(); m_size = new Size( width, height ); m_updated = false; } /// /// キャラクタのタイプを取得します /// public CharacterType Type { get { return m_type; } } /// /// キャラクタの名称を取得します /// public string Name { get { return m_name; } set { m_name = value; } } } }