カテゴリー別

お絵描き、デザイン

写真、動画関連ソフト

アメーバピグ専用ソフト

ホームページ関連

画像処理

スキャナー用

SEO 関連

お楽しみ

その他

過去ログ

2012年10月28日(日)

XNA 4.0 の SpriteBatch で新しく導入された BlendState クラス

写真閲覧ソフト: ミルノ PC フォトフレーム 1.5 では、
利用する XNA フレームワーク (グラフィック表示用ライブラリ) を
XNA 3.1 から、XNA 4.0 Refresh に変更しました。

そのときに、ちょっと苦労した点を、後進の方向けに、ご紹介。
この記事は、XNA 4.0 の BlendState クラスについてです。

XNA 4.0 では、SpriteBatch.Begin の引数から、SpriteBlendMode
が無くなり、かわりに、BlendState が導入されています。

なんとなく、SpriteBlendMode.AlphaBlend に対応するのは、
BlendState.AlphaBlend のような気がしますが、違います。

XNA 4.0 では、3.1 と異なり、基本的に、
アルファ値が色情報にかけられたものを色の基本単位としています。
BlendState.AlphaBlend は、アルファ値が色にかけられた
状態の Texture2D を描画するのに使います。

無理やり (XNA 4.0 の思想にさからって)、アルファ値が色情報に
掛けられていないものを描画するには、BlendState.NonPremultiplied
を指定します。

BlendState.NonPremultiplied を指定すれば、すべて解決かというと、
そうでもありません。1 つは、XNA 3.1 では、SpriteBatch.Draw で、
アルファ値を指定して画像を描く場合、Color(255, 255, 255, alpha)
を渡していいましたが、XNA 4.0 では、Color(alpha, alpha, alpha, alpha)
を渡す必要があります。

もう 1 つは、BlendState.NonPremultiplied が、

AlphaBlendFunction = BlendFunction.Add,
ColorBlendFunction = BlendFunction.Add,
ColorSourceBlend = Blend.SourceAlpha,
AlphaSourceBlend = Blend.SourceAlpha,
ColorDestinationBlend = Blend.InverseSourceAlpha,
AlphaDestinationBlend = Blend.InverseSourceAlpha,

と定義されているので、出力先のアルファ値が正しく計算されません。
正しくは、AlphaSourceBlend = Blend.One です。

描画先が画面の場合は、出力アルファ値は見た目に影響を与えないので、
問題ありませんが、RenderTarget2D に描画する場合、
描画先の Texture2D のアルファ値はおかしなものになります。
例えば、不透明な画像に透明な画像を描くと透明になったりします。

下のような BlendState を指定すれば、
出力アルファ値も正しくなりますが、Reach プロファイルでは
使えないみたいです。(HiDef プロファイルが必要)

public static readonly BlendState _NonPremultiplied = new BlendState()
{
	AlphaBlendFunction = BlendFunction.Add,
	ColorBlendFunction = BlendFunction.Add,
	ColorSourceBlend = Blend.SourceAlpha,
	AlphaSourceBlend = Blend.One,
	ColorDestinationBlend = Blend.InverseSourceAlpha,
	AlphaDestinationBlend = Blend.InverseSourceAlpha,
};

HiDef プロファイルを使用するには、Shader Model 3.0
に対応したグラフィックカードが必要で、敷居が高そうです。

実際、父親の結構新しめの、ノート PC でも動きませんでした。

そこで、ミルノでは、出力アルファを無視することで、
BlendState.NonPremultiplied をうまく乗りこなすことにしています。

XNA 4.0 を利用した高速画像ビューアー、ミルノ PC フォトフレーム
試用は無料なので、是非使ってみてくださいね。


2012年10月28日(日)

XNA 4.0 の、新しい Texture2D 形式

写真閲覧ソフト: ミルノ PC フォトフレーム 1.5 では、
利用する XNA フレームワーク (グラフィック表示用ライブラリ) を
XNA 3.1 から、XNA 4.0 Refresh に変更しました。

そのときに、ちょっと苦労した点を、後進の方向けに、ご紹介。
この記事は、XNA 4.0 の Texture2D 形式についてです。

まず、Texture2D のディフォルトの SurfaceFormat が、
SurfaceFormat.Rgba32 (D3DFMT_A8B8G8R8) から、
SurfaceFormat.Color (D3DFMT_A8R8G8B8)
に変わっています。

また、XNA 4.0 では、SurfaceFormat.Rgba32 自体無いので、
Rgba32 を指定して、Texture2D を作成することもできません。

Texture2D をコンテントパイプラインで作成している場合には、
そのような変換が自動的に行われるので、特に問題は発生しませんが、
Texture2D.SetData などを使って自分で作成している場合には、
コードの変更が必要です。

ちなみに、XNA.4.0 の SurfaceFormat.Color は
Gdi+ の PixelFormat.Format32bppArgb や、
BitmapImage の PixelFormats.Bgra32
とは、並びが違うので、注意が必要です。(赤と青が入れ替わってます)

ミルノ PC フォトフレームでも、BitmapImage から
Texture2D を実行時に作成してます。この変換の際に、
赤を青を入れ換えるのが一番簡単なのですが、遅くなるのは必至です。

そこで、ミルノ PC フォトフレームでは Texture 2D を描画する際に、
カスタム Effect を用意、ピクセルシェーダー機能を使って、
赤と青の成分を入れかえながら描画するようにコードに変更しました。

XNA 4.0 を利用した高速画像ビューアー、ミルノ PC フォトフレーム
試用は無料なので、是非使ってみてくださいね。


2012年02月13日(月)

C#: Ctrl+V などのキーをショートカットキーとして正しく処理するには

ショートカットキーの設定方法は、以前のブログ
(過去ログ:C#: Escape などのキーをショートカットキー (アクセラレータキー) として処理するには)
でご紹介いたしましたが、より詳細な情報をホームページに載せました。

c# ショートカットキーを正しく処理するには? です。

メニューで、Ctrl+V などのショートカットキーを設定すると、
コンボボックスにフォーカスがある場合に、
本来の挙動がオーバーライドされてしまう問題などが解決できます。

.NET アプリケーションでショートカットキーを使用するときに、
役に立つ情報となってますので、是非、お役立てください。

c++ 使いの c# メモ
c# ショートカットキーを正しく処理するには?
c# ショートカットキーを正しく処理するには?(その2)
c# ショートカットキーを正しく処理するには?(その3)


2012年02月10日(金)

.NET 3.5 Keys 一覧表

.NET フレームワーク 3.5 の、System.Windows.Forms.Keys の、
16進、10進表現を表にしてみました。

.NET 3.5 Keys 一覧

この表から、本家 (MSDN) の Keys 列挙体 の表で、
「修飾子キー」「ビット マスク」とついているもののみ特殊で、
あとは普通のキーコードだということがはっきりすると思います。

ご参考にしてください。

c++ 使いの c# メモ
.NET 3.5 Keys 一覧


2011年10月07日(金)

c# ファイナライザーの実行タイミング

c# ファイナライザーの実行タイミングは、かなり早期に行われます。

かなり長い間、気付かずに苦労したので、
後進のために、記事を書きました。

c# ファイナライザーの実行タイミング

詳細は記事を見ていただきたいのですが、
正直、ビックリしました。

デバッグ版とリリース版とで実行タイミングが
こうも異なると非常にデバッグが困難です。

リリース版のファイナライザーの呼び出しタイミングは、
効率的なのでよしとしましょう。

しかし、デバッグ版ではリリース版より早期に
呼び出してくれないと困るんですよねぇ。

デバッグ版では、GC.Collect() を呼び出しても、
ファイナライザーが呼びだされない場所なのに、
リリース版では呼びだされるというのは、
かなりいただけないです。うーん。

まぁ、ネイティブリソースを使用しなければ起こらない
問題なのですが、c# でネイティブリソースを扱うには
c++ より慎重さが必要のようです。

まぁ、常にある程度の慎重さが必要な c++ よりは楽
かもしれませんが。

ミルノ PC フォトフレーム では、バージョン 1.3.2.0 で
この問題に起因する不具合を修正しました。

ミルノでは、マルチスレッドで画像をデコードできます。
なので、2 つ以上の CPU を持つパソコンを最大限活かすには、
ミルノのようなマルチスレッド対応のソフトが最適ですよ。

試用は無料なので、是非、使ってみてくださいね。

画像ビューア: ミルノ PC フォトフレームのダウンロード
画像ビューア: ミルノ PC フォトフレームの更新情報
ご意見・ご要望連絡窓口


2011年07月04日(月)

c# BitmapDecoder を別スレッドで作成するとリソースリークする

別スレッドで、 BitmapDecoderCreate メソッドを呼び出すと、
リソースリークが発生します。

2012/5/7 追記
よりよい解決法を発見したので、ホームページに掲載しました。
c# BitmapDecoder を別スレッドで作成するとリソースリークする
をご覧ください。

どうやら、BitmapDecoder.Create 内で、
RegisterClassEx を使用してウィンドウクラスが登録されるようですが、
(少なくともすぐには) 開放されないみたいです。

.NET フレームワーク 3.5 で見られた現象ですが、
別のバージョンについては調べてません。

ウィンドウクラスの解法が行われず、リソースが足りない状態に陥ると、
IE などのほとんどのアプリケーションが起動できなくなります。

また、発生するエラーは、
「このコマンドを実行するのに十分な記憶域がありません」
ですが、メモリーがいくらあっても発生します。

↓がテスト用のコードです。

using System;
using System.Threading;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace UnitTest
{
  class TestBitmapDecoder
  {
    const string path = @"C:\Windows\Web\Wallpaper\img1.jpg";
    const BitmapCreateOptions createOption = BitmapCreateOptions.None;
    const BitmapCacheOption cacheOption = BitmapCacheOption.Default;

    public void Test()
    {
      int count = 0;
      bool error = false;

      while (!error)
      {
        Thread thread = new Thread(new ThreadStart(delegate
          {
            try
            {
              BitmapDecoder decoder = BitmapDecoder.Create(
                new Uri(path), createOption, cacheOption
              );
              ++ count;
              if (count % 100 == 0)
                Console.WriteLine(count);
            }
            catch (Exception e)
            {
              Console.WriteLine(count);
              Console.WriteLine(e.ToString());
              error = true;
            }
#if false
            finally
            {
              Dispatcher.CurrentDispatcher.InvokeShutdown();
            }
#endif
          }));

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
      }
    }
  }
}

テスト用のクラスを呼び出すコード↓。

using System;

namespace UnitTest
{
  class Program
  {
    static void Main(string[] args)
    {
      new TestBitmapDecoder().Test();
    }
  }
}

System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command を参考にしています。

参考のページのコードでは、ロック不要なのにロックしてたり、
反面、catch 内で、はロックしてなかったりして意味不明なのですがw
参考になりました。

Windows Vista 32 ビットバージョンでの出力は、↓のようになります。

100
.
.
.
15600
15674
System.ComponentModel.Win32Exception: このコマンドを実行するのに十分な記憶域がありません。
   場所 MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
   場所 MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   場所 MS.Win32.MessageOnlyHwndWrapper..ctor()
   場所 System.Windows.Threading.Dispatcher..ctor()
   場所 System.Windows.Threading.Dispatcher.get_CurrentDispatcher()
   場所 System.Windows.Threading.DispatcherObject..ctor()
   場所 System.Windows.Media.Imaging.BitmapDecoder..ctor(SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Boolean insertInDecoderCache, Boolean isOriginalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle)
   場所 System.Windows.Media.Imaging.JpegBitmapDecoder..ctor(SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Boolean insertInDecoderCache, Boolean originalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle)
   場所 System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
   場所 System.Windows.Media.Imaging.BitmapDecoder.Create(Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy)
   場所 System.Windows.Media.Imaging.BitmapDecoder.Create(Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
   場所 UnitTest.TestBitmapDecoder.<>c__DisplayClass2.b__0() 場所 X:\Original\cs\Common\UnitTest\TestBitmapDecoder.cs:行 25

回避の方法は、スレッド終了前に、
Dispatcher.CurrentDispatcherInvokeShutdown() を呼ぶことです。
(コード中で #if false によりコメントアウトされている部分)

やはり、32 ビットバージョンの、ウィンドウズでは、
ウィンドウクラスの登録は、16000 くらいが限界みたいですね。

ちなみに、Windows XP の 64 ビットバージョンでは、62685 くらいで
同様のエラーが起こりましたが、プログラムを終了させても、
他のプログラムはほとんどまともに動かなくなりました。

Vista では、プログラムを終了させれば他のプログラム、
少なくとも終了後に起動したプログラムはまともに動作したので、
そこら辺は進化してるみたいですね。

System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command と似てますけど、
この問題と解法を公開するのは世界で最初っぽいですねー。

ミルノ PC フォトフレーム では、
バージョン 1.2.2.2 でこの不具合を修正済みです。

2つ以上の CPU を持つパソコンを最大限活かすには、
ミルノ PC フォトフレームのようなマルチスレッド対応のソフトが最適ですよ。

試用は無料なので、是非、使ってみてくださいね。

画像ビューア: ミルノ PC フォトフレームのダウンロード
画像ビューア: ミルノ PC フォトフレームの更新情報
ご意見・ご要望連絡窓口


2011年06月19日(日)

VC# 2008 クラスライブラリを静的にリンクする方法

Visual C# 2008 Express Edition では、
.NET フレームワークが用意しているクラスライブラリを
実行形式に静的にリンクする手段は用意されていないようですが、
自作のクラスライブラリ (ソースコードがある) なら
強引に静的リンクすることが可能です。

あまり推奨されていない方法なので、
少しだけ厄介なことが起こりましたが、
配るファイルを exe 1 つにしたい場合には役に立つかと思います。
詳細は下記リンク先に書きました。

c# クラスライブラリの静的リンクによる参照

まぁ、あまりこだわりがない場合は、普通に dll として参照したほうが
圧倒的にカンタンです。↓普通の方法。

c# クラスライブラリの参照方法

↓.NET プログラマなら、.NET 検索エンジンをお気に入りバー
に入れておくと、便利ですよー。

.NET 検索エンジン


2011年06月19日(日)

VC# 2008 クラスライブラリの作成と利用方法

Visual C# 2008 Express Edition を例に、
C# でクラスライブラリを利用する方法
を解説するページを作成しました。

c# クラスライブラリの作成と利用方法

ミルノが c# で作成したプログラムも多くなってきて、
コードをうまく共有しないと、
生産効率が悪くなってきました。

これまでは、cs ファイルをコピーしてコードを再利用していました。
まぁそれでも、プロジェクトが 2 つくらいだとそれほど大変ではなかったのですが、
3 つ 4 つとなってくると、同期が面倒というかほぼ不可能になってきました。

そこでコードを共有するようにプログラムを組み替えたのですが、
その際いろいろやってみた結果をまとめたのが、
c# クラスライブラリの作成と利用方法 です。

これから C# で自作のクラスライブラリを使ってみよう!
という場合には役に立つと思います。

ミルノのいち押しソフト↓。
(3 つだけど・・・)

XNA を利用した新画像ビューア: ミルノ PC フォトフレーム
ローカルの HTML をカンタンに見られる: ローカルブラウザ
複雑な縁取り文字をカンタンに作成できる: 文字に縁取りするノ


2011年05月27日(金)

c# 仮想 ListView で選択項目削除の高速化

仮想 ListView については前回のブログを見てください。
(過去ログ:c# 仮想 ListView で全て選択するには)

仮想 ListView で、選択項目を削除しようとして以下のようなコードを書くと
異常に遅いです。

int count = listView1.SelectedIndices.Count;
for (int i = count - 1; i >= 0; -- i)
  items.RemoveAt(listView1.SelectedIndices[i]);

なんと、環境にもよりますが、1万件の削除に 43 秒もかかりました !
しかも原因は RemoveAt ではなくて、SelectedIndices[i] です。

なんでやねーんw。

なので、下のように修正すると異常にはやくなります。

int count = listView1.SelectedIndices.Count;
int[] indices = new int[count];
listView1.SelectedIndices.CopyTo(indices, 0);
for (int i = count - 1; i >= 0; -- i)
  items.RemoveAt(indices[i]);

なんと、自分の環境では、10 ミリ秒。

ぴゃーわけわからん。(by よゐこ、はまぐちぇ)

一応、↓がスクリーンショットとデザイナで生成してない部分の全コードです。

リストビューテスト用フォーム

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;

namespace ListViewTest
{
  partial class Form1 : Form
  {
    List items = new List();

    public Form1()
    {
      InitializeComponent();
      
      listView1.HideSelection = false;
      listView1.VirtualMode = true;
      listView1.View = System.Windows.Forms.View.Details;

      listView1.Columns.Add(new ColumnHeader());
      listView1.Columns[0].Text = "no";

      listView1.RetrieveVirtualItem += listView1_RetrieveVirtualItem;
      buttonInit.Click += buttonInit_Click;
      buttonSelectAll.Click += buttonSelectAll_Click;
      buttonDelete.Click += buttonDelete_Click;
    }

    private void listView1_RetrieveVirtualItem(
      object sender, RetrieveVirtualItemEventArgs e
    )
    {
      e.Item = items[e.ItemIndex].CreateListViewItem();
    }

    private void buttonInit_Click(object sender, EventArgs e)
    {
      listView1.BeginUpdate();

      items.Clear();
      for (int i = 0; i < 10000; ++ i)
        items.Add(new VirtualItem(i));
      listView1.VirtualListSize = items.Count;

      listView1.EndUpdate();
    }

    private void buttonSelectAll_Click(object sender, EventArgs e)
    {
      Stopwatch watch = Stopwatch.StartNew();

      listView1.BeginUpdate();

      listView1.SelectedIndices.Clear();
      for (int i = 0; i < items.Count; ++ i)
        listView1.SelectedIndices.Add(i);

      listView1.EndUpdate();

      MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // 236 ミリ秒
    }

    private void buttonDelete_Click(object sender, EventArgs e)
    {
      Stopwatch watch = Stopwatch.StartNew();

      listView1.BeginUpdate();

#if true
      // 43 秒もかかる !! //
      int count = listView1.SelectedIndices.Count;
      for (int i = count - 1; i >= 0; -- i)
        items.RemoveAt(listView1.SelectedIndices[i]);
#else
      // 10 ミリ秒 !! //
      int count = listView1.SelectedIndices.Count;
      int[] indices = new int[count];
      listView1.SelectedIndices.CopyTo(indices, 0);
      for (int i = count - 1; i >= 0; -- i)
        items.RemoveAt(indices[i]);
#endif

      listView1.VirtualListSize = items.Count;
      listView1.EndUpdate();
      MessageBox.Show(watch.ElapsedMilliseconds.ToString());
    }
  }

  class VirtualItem
  {
    public int no;
    
    public VirtualItem(int no)
    {
      this.no = no;
    }

    public ListViewItem CreateListViewItem()
    {
      ListViewItem item = new ListViewItem(no.ToString());
      return item;
    }
  }
}

XNA を用いた最新の画像ビューア: ミルノ PC フォトフレーム
ローカルの HTML をカンタンに見られる: ローカルブラウザ
複雑な縁取り文字をカンタンに作成できる: 文字に縁取りするノ


2011年05月27日(金)

c# 仮想 ListView で全て選択するには

.NET の ListView で、 VirtualMode を true に設定すると、
ListView を仮想モードで使用することができます。

ListView を仮想モードで使用すると、ListView には
とりあえず表示に必要なアイテムのみ作成されるので、
特に多くのデータを扱う必要のある場合、より高速なレスポンスを
期待することができます。

リストビューを仮想リストビューとして使用するには、
VirtualMode = true にして、 VirtualListSize を設定すると、
RetrieveVirtualItem イベント が、必要に応じて発生するので、
必要とされるアイテムを生成して返せば OK です。

リストビューテスト用フォーム

下がサンプルプログラムです。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;

namespace ListViewTest
{
  partial class Form1 : Form
  {
    List items = new List();

    public Form1()
    {
      InitializeComponent();

      listView1.HideSelection = false;
      listView1.VirtualMode = true;
      listView1.View = System.Windows.Forms.View.Details;

      listView1.Columns.Add(new ColumnHeader());
      listView1.Columns[0].Text = "no";

      listView1.RetrieveVirtualItem += listView1_RetrieveVirtualItem;
      buttonInit.Click += buttonInit_Click;
      buttonSelectAll.Click += buttonSelectAll_Click;
    }

    private void listView1_RetrieveVirtualItem(
      object sender, RetrieveVirtualItemEventArgs e
    )
    {
      e.Item = items[e.ItemIndex].CreateListViewItem();
    }

    private void buttonInit_Click(object sender, EventArgs e)
    {
      listView1.BeginUpdate();

      items.Clear();
      for (int i = 0; i < 10000; ++ i)
        items.Add(new VirtualItem(i));
      listView1.VirtualListSize = items.Count;

      listView1.EndUpdate();
    }

    private void buttonSelectAll_Click(object sender, EventArgs e)
    {
      Stopwatch watch = Stopwatch.StartNew();

      listView1.BeginUpdate();

      listView1.SelectedIndices.Clear();
      for (int i = 0; i < items.Count; ++ i)
        listView1.SelectedIndices.Add(i);

      listView1.EndUpdate();

      MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // 236 ミリ秒
    }
  }

  class VirtualItem
  {
    public int no;
    
    public VirtualItem(int no)
    {
      this.no = no;
    }

    public ListViewItem CreateListViewItem()
    {
      ListViewItem item = new ListViewItem(no.ToString());
      return item;
    }
  }
}

Form1() の InitializeComponent() 以降のコードはデザイナーの
プロパティやイベントでも設定できるので、そちらを使用した方が簡単だと思います。

buttonSelectAll_Click では、リストビューの項目を全て選択しています。
仮想モードでは、 SelectedItems が使用できませんが、
SelectedIndices は使用することができるので、
SelectedIndices に全てのインデックスを設定するだけで OK です。

XNA を用いた最新の画像ビューア: ミルノ PC フォトフレーム
ローカルの HTML をカンタンに見られる: ローカルブラウザ
複雑な縁取り文字をカンタンに作成できる: 文字に縁取りするノ


<< | 2/3PAGES | >>