私の理解が正しければ、ここでの問題は、パレット自体ではなく、ピクセル値 (パレット インデックス) をコピーしていることです。純粋な C# でパレットをコピーする方法が見つかりませんでしたが、P/Invoke と DirectX の代替手段を使用して...どちらも Windows への依存関係を追加するため、P/Invoke を選択しました。 DirectX に依存します。
2つの方法をご紹介します...
最初の方法:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern IntPtr SelectPalette(
IntPtr hdc,
IntPtr htPalette,
bool bForceBackground);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int RealizePalette(IntPtr hdc);
private void ScreenShot()
{
IntPtr htPalette = Graphics.GetHalftonePalette();
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc = graphics.GetHdc();
SelectPalette(hdc, htPalette, true);
RealizePalette(hdc);
graphics.ReleaseHdc(hdc);
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
GetHalftonePalette
この方法は、適切なパレットを提供することに依存します。私はパレットでテストしたことがありません (私は若すぎます)... そこで、ウィンドウが特定の条件でパレットを自動的に処理する必要があることに基づいて、2 番目のメソッドを作成することにしました。注: CopyFromScreen への呼び出しを using ブロックの先頭に移動する方がよいかどうか、または RealizePalette への呼び出しを無視する方がよいかどうかはわかりません (私は若すぎます)。
2番目の方法:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
private void ScreenShot()
{
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
using (var screenshot = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var fromHwnd = Graphics.FromHwnd(new IntPtr(0)))
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc_screen = fromHwnd.GetHdc();
IntPtr hdc_screenshot = graphics.GetHdc();
BitBlt(hdc_screenshot, 0, 0, width, height, hdc_screen, 0, 0, 0x00CC0020); /*SRCCOPY = 0x00CC0020*/
graphics.ReleaseHdc(hdc_screenshot);
fromHwnd.ReleaseHdc(hdc_screen);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
どちらの方法も通常の状態で機能します。テストを行ってください。元のパレットを複製することを追加して2番目の方法を実行する必要があるかもしれません(つまり、... Windowsがそれらを自動的に処理しない場合)が、方法がわかりませんそれをして(私は若すぎる)、この方法がうまくいくことを願っています。
最後に、追加の依存関係を許容し、Windows とは異なるものにしたい場合は、GTK# (Windows から利用可能なバージョンがあります) が適切なオプションです。Mono C# でスクリーンショットを撮るには? を参照してください。同様の問題に対処し、GTK# のサンプル コードを提供します。
注:
次のコードはここでうまく機能するようですが、このコードによって例外が発生しますか?
System.Drawing.Image image = Clipboard.GetImage();
image.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);