🐡

ColorPaletteクラスにpublic constructorが登場!

2024/05/25に公開

.NET 9でColorPaletteクラスにpublic constructorが登場します。何が嬉しいのか説明します。

.NET Framework 1.0時代より、画像を扱うにはBitmapクラスを使用します。これはWindows XPで標準搭載されたGDI+ライブラリを.NETで扱うためのクラスです。ここで256色パレットカラーの場合、Bitmap.PatelleプロパティColorPaletteクラスにアクセスします。Bitmap.Patelleプロパティはこのような実装になっています。

https://github.com/dotnet/winforms/blob/v8.0.5/src/System.Drawing.Common/src/System/Drawing/Image.cs#L701-L750

つまり、Bitmap.Patelleプロパティにアクセスすると新しいColorPaletteインスタンスを生成して返してきます。これが何を意味するかというと、

void SetRed(Bitmap bitmap) {
  bitmap.Palette.Entries[0] = Color.Red;
}

と値を設定しても、新しいColorPaletteインスタンスが変更されるだけで元のbitmapには反映されません。正しくは、

void SetRed(Bitmap bitmap) {
  var palette = bitmap.Palette;
  palette.Entries[0] = Color.Red;
  bitmap.Palette = palette;
}

ColorPaletteインスタンスを保存しておき、bitmap.Paletteに書き戻してやる必要があります。
つまり、ColorPaletteインスタンスを強く意識した操作が必要になります。ではColorPaletteクラスとはどういったものでしょうか? ソースコードを見ると…

https://github.com/dotnet/winforms/blob/v8.0.5/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorPalette.cs#L8-L96

…はい、概ねColor配列を持つだけでした。にもかかわらず、コンストラクターがinternalと隠されています。

まとめると、256色パレットカラーを扱う場合、

  • ColorPaletteインスタンスを意識する
  • ColorPaletteクラスは単にColor配列を持つだけ
  • ColorPaletteコンストラクターは公開されていない

という頭お菓子なる設計に立ち向かう必要があります。具体的には

ColorPalette CreatePalette() {
  using var bitmap = new Bitmap(1, 1, PixelFormat.Indexed);
  return bitmap.Palette;
}

とか、

ColorPalette CreatePalette() {
  return (ColorPalette)typeof(ColorPalette).InvokeMember(
    null,
    BindingFlags.NonPublic | BindingFlags.CreateInstance,
    null,
    null,
    new object[]{ 256 }
  );
}

する必要がありました。

これが.NET 9からは

ColorPalette CreatePalette() {
  return new ColorPalette(new Color[256]);
}

と書けるようです!


従来 .NET Frameworkはソースコードが公開されておらず、試行錯誤とildasmで調べたものです。referencesourceとかできる前ね。(遠い目)

Discussion