using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace TIASshot {
/// <summary>
/// Bitmap 操作の共有ユーティリティ。
/// プレビュー表示経路で毎フレーム発生する Bitmap の複製処理を高速化するために,
/// GDI+ の DrawImage 経路(<see cref="Bitmap(Image)"/>)ではなく,
/// LockBits による生画素の直接コピー(memcpy 相当)を提供する。
/// </summary>
internal static class BitmapUtil {
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
/// <summary>
/// Bitmap を高速に複製する。
/// 入力と同じ PixelFormat・サイズの新規 Bitmap を確保し,LockBits で取得した
/// 生画素バッファをネイティブ memcpy(RtlMoveMemory)で丸ごとコピーする。
/// 生成される Bitmap は入力とは独立した画素バッファを持つため,
/// 呼び出し元スレッドとは別のスレッド(UI スレッド等)で安全に保持・Dispose できる
/// (<see cref="Bitmap(Image)"/> によるフルコピーと同じ所有権モデル)。
/// </summary>
/// <param name="src">複製元の Bitmap(読み取りのみ・変更しない)</param>
/// <returns>src と独立した画素バッファを持つ新規 Bitmap</returns>
public static Bitmap FastClone(Bitmap src) {
if (src == null) return null;
// インデックスカラー系(8bpp 以下)はパレットを保持する必要があり,
// LockBits の単純コピーだけではパレット情報が失われるため,
// 安全網として従来どおりの GDI+ フルコピーにフォールバックする。
// プレビュー画像(Mat.ToBitmap() 由来の 24bppRgb)は通常この分岐に入らない。
if ((src.PixelFormat & PixelFormat.Indexed) != 0) {
return new Bitmap(src);
}
var rect = new Rectangle(0, 0, src.Width, src.Height);
var dst = new Bitmap(src.Width, src.Height, src.PixelFormat);
BitmapData srcData = null;
BitmapData dstData = null;
try {
srcData = src.LockBits(rect, ImageLockMode.ReadOnly, src.PixelFormat);
dstData = dst.LockBits(rect, ImageLockMode.WriteOnly, dst.PixelFormat);
// ストライドは通常一致するが,念のため行ごとに小さい方のバイト数だけコピーする
// (負ストライド/パディング差があっても src・dst それぞれの範囲を超えない堅牢な実装)
int bytesPerRow = Math.Min(Math.Abs(srcData.Stride), Math.Abs(dstData.Stride));
var count = (UIntPtr)bytesPerRow;
for (int y = 0; y < src.Height; y++) {
IntPtr srcRow = IntPtr.Add(srcData.Scan0, y * srcData.Stride);
IntPtr dstRow = IntPtr.Add(dstData.Scan0, y * dstData.Stride);
CopyMemory(dstRow, srcRow, count);
}
} finally {
// src は ReadOnly ロックのみなので UnlockBits は元データを変更しない(src は破損しない)
if (srcData != null) src.UnlockBits(srcData);
if (dstData != null) dst.UnlockBits(dstData);
}
return dst;
}
}
}