lipsync/trunk/LipSync/Editor/Character3.cs

740 lines
28 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
}
}
}
}