none
Image クラスのパレットを変更する方法 RRS feed

  • 質問

  • PixelFormat プロパティが PixelFormat.Format8bppIndexed である Image クラスのパレット(Image.Palette プロパティ)を任意のものに変更する方法を教えてください。

     

    ColorPalette クラスはコンストラクタを持たないので、new で新たに生成して Image.Palette プロパティに代入することができません。

     

    また、ColorPalette.Entries プロパティは読み取り専用ですし、get で取得した配列の要素に Color 構造体の値を代入する式を書いても、実際は代入されませんでした。しかし、ColorPalette.Flags プロパティも読み取り専用なので、エントリのみを変更できても恐らく意味がないです。

     

    どうすればいいのでしょうか。

    2007年11月23日 19:24

回答

  •  

    コード ブロック

            Dim cp As System.Drawing.Imaging.ColorPalette
            cp = bitmap.Palette
            cp.Entries(0) = Color.XXX

            Me.bitmap.Palette = cp

     

     

    で、私のところではできますが、これでできませんか?

     

    Paletteプロパティはコピーを返しますので、

    Me.bitmap.Palette.Entries(x) = Color.XXX

    ではだめです。

    2007年11月23日 21:18
  • #既にご回答・ご解決されていますが…

     

    本当ですね。このことはドキュメントには書いてありませんね。(ちょっと調べてみた限り)

    自分が以前使用したときははまりませんでしたが、はまってしまう方がいてもおかしくないですね。

     

    サポートサイトのColorPaletteを使用するサンプルでも設定し戻すようにしてますね。

     

    How to save a .gif file with a new color table by using Visual C#

    http://support.microsoft.com/kb/319061/EN-US/

     

    結局、メンバを設定してみても変わらないことから、推測できるのではないでしょうか。

     

    デバッガでブレークポイントを張ってみて、もとのImageのインスタンス(imgとします)のPalleteプロパティと、そこから取得したColorPaletteのインスタンス(cpとします)の参照をウォッチ式ウィンドウでみながら、cp.Entriesへの設定の行をステップ実行で通り過ぎたとき、もとのimgのPalleteプロパティのEntriesの中身が変わっていないことから推測できます。

     

    また、img.Palette.GetHashCode()が毎回違う値を返すことからもそのような挙動しているのだと確信が持てます。

    2007年11月24日 5:10
  •  豊田 芳彰 さんからの引用

    ところで、「Paletteプロパティはコピーを返しますので」 というのは MSDN ライブラリの Image.Palette プロパティの項目には書かれていないのですが、コピーを返すのか参照を返すのか、どのようにして分かったのでしょうか。コピーを返すような挙動をするから、ということでしょうか。

     

    プロパティが返す値は元のクラスとの関連性を持ってる状態で返されるわけではありませんので、

    いちいち考えないといけません。

     

    値型は常にコピーで関連性を持ちようがありません。

    参照型でも、値型のように扱われるクラスは関連性を維持しないように作ると思います。(Control.Textなど。)

    オブジェクトが大きい場合はコピーすると大変ですからそのまま返すでしょうし、

    大きくてもメソッドが無いようなクラスならコピーの場合も十分ありえます。

    返したオブジェクトをいじられると困る場合もコピーを返すはずです。

    もともと保持していなくて、動的に生成する場合はコピーを返すのが楽な場合もあります。

     

    ImageはGdi+のImageクラスをラップしたクラスで、

    Gdi+のImageクラスからパレットを取得・設定するときはGetPalette/SetPaletteを使います。

    Paletteを内部で保持しなくてもImageクラスは問題なく動作しますから、

    Paletteプロパティを呼ぶたびにGetPaletteを呼ぶ実装が楽で軽いだろうなと思います。

    そう考えると、Paletteプロパティはコピーを返すと予想でき、予想できれば検証は簡単です。

     

    アンマネージリソースを扱っていたり、他のライブラリをラップしていたりする場合は

    いろいろ事情があるでしょうから、特に気をつけないといけませんね。

    ColorPaletteクラスにコンストラクタが無かったりするのもその辺の事情じゃないでしょうか。

    2007年11月24日 18:01

すべての返信

  •  

    コード ブロック

            Dim cp As System.Drawing.Imaging.ColorPalette
            cp = bitmap.Palette
            cp.Entries(0) = Color.XXX

            Me.bitmap.Palette = cp

     

     

    で、私のところではできますが、これでできませんか?

     

    Paletteプロパティはコピーを返しますので、

    Me.bitmap.Palette.Entries(x) = Color.XXX

    ではだめです。

    2007年11月23日 21:18
  • あ、なるほど。Image.Palette プロパティから ColorPalette クラスのインスタンスをコピーして、加工後に戻せばよかったんですね。戻すというプロセスが足りなかったようです。

     

    ところで、「Paletteプロパティはコピーを返しますので」 というのは MSDN ライブラリの Image.Palette プロパティの項目には書かれていないのですが、コピーを返すのか参照を返すのか、どのようにして分かったのでしょうか。コピーを返すような挙動をするから、ということでしょうか。
    2007年11月24日 3:30
  • #既にご回答・ご解決されていますが…

     

    本当ですね。このことはドキュメントには書いてありませんね。(ちょっと調べてみた限り)

    自分が以前使用したときははまりませんでしたが、はまってしまう方がいてもおかしくないですね。

     

    サポートサイトのColorPaletteを使用するサンプルでも設定し戻すようにしてますね。

     

    How to save a .gif file with a new color table by using Visual C#

    http://support.microsoft.com/kb/319061/EN-US/

     

    結局、メンバを設定してみても変わらないことから、推測できるのではないでしょうか。

     

    デバッガでブレークポイントを張ってみて、もとのImageのインスタンス(imgとします)のPalleteプロパティと、そこから取得したColorPaletteのインスタンス(cpとします)の参照をウォッチ式ウィンドウでみながら、cp.Entriesへの設定の行をステップ実行で通り過ぎたとき、もとのimgのPalleteプロパティのEntriesの中身が変わっていないことから推測できます。

     

    また、img.Palette.GetHashCode()が毎回違う値を返すことからもそのような挙動しているのだと確信が持てます。

    2007年11月24日 5:10
  • やはり、はっきりとした記述はドキュメントにはなさそうですね(最初、バグかと思いました)。

     

    ありがとうございました。
    2007年11月24日 7:08
  •  豊田 芳彰 さんからの引用

    ところで、「Paletteプロパティはコピーを返しますので」 というのは MSDN ライブラリの Image.Palette プロパティの項目には書かれていないのですが、コピーを返すのか参照を返すのか、どのようにして分かったのでしょうか。コピーを返すような挙動をするから、ということでしょうか。

     

    プロパティが返す値は元のクラスとの関連性を持ってる状態で返されるわけではありませんので、

    いちいち考えないといけません。

     

    値型は常にコピーで関連性を持ちようがありません。

    参照型でも、値型のように扱われるクラスは関連性を維持しないように作ると思います。(Control.Textなど。)

    オブジェクトが大きい場合はコピーすると大変ですからそのまま返すでしょうし、

    大きくてもメソッドが無いようなクラスならコピーの場合も十分ありえます。

    返したオブジェクトをいじられると困る場合もコピーを返すはずです。

    もともと保持していなくて、動的に生成する場合はコピーを返すのが楽な場合もあります。

     

    ImageはGdi+のImageクラスをラップしたクラスで、

    Gdi+のImageクラスからパレットを取得・設定するときはGetPalette/SetPaletteを使います。

    Paletteを内部で保持しなくてもImageクラスは問題なく動作しますから、

    Paletteプロパティを呼ぶたびにGetPaletteを呼ぶ実装が楽で軽いだろうなと思います。

    そう考えると、Paletteプロパティはコピーを返すと予想でき、予想できれば検証は簡単です。

     

    アンマネージリソースを扱っていたり、他のライブラリをラップしていたりする場合は

    いろいろ事情があるでしょうから、特に気をつけないといけませんね。

    ColorPaletteクラスにコンストラクタが無かったりするのもその辺の事情じゃないでしょうか。

    2007年11月24日 18:01
  • > もともと保持していなくて、動的に生成する場合はコピーを返すのが楽な場合もあります。

     

    この辺りまでは自分でクラスを作る時も気に掛ける一般的なのことなのですが、

     

    > ImageはGdi+のImageクラスをラップしたクラスで、

     

    この辺りは初めて知りました。処理を実現している仕組みが分かってしまえば、コピーを返すのも当たり前だって分かりますね。

     

    見落としていたドキュメントに記述されていたら教えてもらってメモしておこうという気持ちで再度質問したのですけど、詳しく教えてくれて、ありがとうございました。

    2007年11月25日 8:31
  • C#で、
    下記のようなコードを書きうまく行かず、このページにたどり着きました。

        for (int i = 0; i <= 255; i++)
        {
            bitmap.Palette.Entries[i] = Color.FromArgb(i, i, i);
        }

    このページを参考にして下記のように書き換えたところ、うまく行きました。
        System.Drawing.Imaging.ColorPalette colorPalette = bitmap.Palette;
       
        for (int i = 0; i <= 255; i++)
        {
            colorPalette.Entries[i] = Color.FromArgb(i, i, i);
        }
        bitmap.Palette = colorPalette;

    ところが、VBでは下記のようなコードで正常に動作しました。

    For i As Integer = 0 To 255
        bitmap.Palette.Entries(i) = Color.FromArgb(i, i, i)
    Next

    何故、VBとC#で挙動が異なるのでしょうか?

    Visual Studio 2003を使用しています。

     

    2010年10月26日 9:30
  • 何故、VBとC#で挙動が異なるのでしょうか?

    主題はここですよね?
    であれば、昔のスレッドにつけるのではなく、新しくスレッドを作って、このスレッドのリンクを参考に書いて、改めて質問してください。

    VB/C# のコンパイラの挙動の差は類似の質問とは言えないことと、3 年前の古いスレッドを使い回す積極的な理由はないことからです。
     


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年10月26日 13:42
    モデレータ