lipsync/trunk/LipSync/Editor/Character3.cs

740 lines
28 KiB
C#
Raw Normal View History

/*
* 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 {
/// <summary>
/// キャラクタを取り扱うクラス。
/// 第3世代
/// </summary>
[Serializable]
public class Character3 : ICloneable, IDisposable {
string m_name;
CharacterType m_type;
ImageEntry[] m_basic = new ImageEntry[9];
List<ImageEntry> 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;
/// <summary>
/// 使用上の注意など
/// </summary>
[OptionalField]
string m_lisence;
#region public static field
/// <summary>
/// ビルトイン・キャラクタ。
/// </summary>
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<int, string> slist = new SortedList<int, string>();
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<ImageEntry>();
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 "";
}
/// <summary>
/// ファイルに保存する
/// </summary>
/// <param name="path"></param>
/// <param name="pack"></param>
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<ImageEntry> ) );
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;
}
}
/// <summary>
/// xmlファイルからのコンストラクタ
/// </summary>
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<ImageEntry> ) );
res.m_another = (List<ImageEntry>)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;
}
/// <summary>
/// 第2世代目のCharacterからのコンバート
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
public Character3( Character character ) {
List<ImageEntry> basic = new List<ImageEntry>();
List<ImageEntry> another = new List<ImageEntry>();
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<ImageEntry>( 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<int> type = new List<int>();
int count = -1;
foreach ( ImageEntry img in this ) {
count++;
if ( img.IsDefault ) {
type.Add( count );
}
}
return Face( type.ToArray() );
}
}
public IEnumerator<ImageEntry> 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<ImageEntry>( another );
} else {
m_another = new List<ImageEntry>();
}
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;
}
/// <summary>
/// キャラクタのタイプを取得します
/// </summary>
public CharacterType Type {
get {
return m_type;
}
}
/// <summary>
/// キャラクタの名称を取得します
/// </summary>
public string Name {
get {
return m_name;
}
set {
m_name = value;
}
}
}
}