C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

パレットの異なる2枚のPNGファイルを1枚に合成して表示 /紅 (24/05/26(Sun) 14:40) #103125
Re[1]: パレットの異なる2枚のPNGファイルを1枚に合成して表示 /KOZ (24/05/26(Sun) 18:21) #103130
  └ Re[2]: パレットの異なる2枚のPNGファイルを1枚に合成して表示 /紅 (24/05/26(Sun) 20:04) #103131 解決済み


親記事 / ▼[ 103130 ]
■103125 / 親階層)  パレットの異なる2枚のPNGファイルを1枚に合成して表示
□投稿者/ 紅 (5回)-(2024/05/26(Sun) 14:40:01)

分類:[.NET 全般] 

2024/05/26(Sun) 14:43:10 編集(投稿者)

開発環境:VS2022の.NetFramework4.8
言語:C#(WPF)

作成中のビューワーにWriteableBitmapを使い2枚の画像を合成した処理があります。
JpegやWebPファイルは問題なく処理できます。
しかし、PNGファイルだと左右で違うパレットのため片方の画像が正常に表示できません。

前回、経緯を省いて質問したせいで回答者さんに負担になりましたので説明します。
2枚の画像をCanvasに表示するだけなら、Canvasを二つ用意して、それぞれ表示させればいいだけですが。。。
このビューワーはPDFのマッキーズームと同等の機能を持っています。
(選択範囲のみを拡大して表示するような機能です)
そのためCanvasを2つ用意、もしくは一つのCanvasに2枚の画像を個別に表示するより、画像を1枚に合成した方が楽なので、そうしています。

パレットの異なるPNGファイルをWriteableBitmapクラスを使い、1枚の画像に合成する良い方法はありませんか?
サンプルは次の通りです。


// 引数のlは左画像の情報、rは右画像の情報
async Task<(WriteableBitmap bmp, (double Width, double Height) imgSize)> WriteImage(
(int Width, int Height, int Stride, byte[] Data) l,
(BitmapSource Image, int Stride, byte[] Data) r,
(double Width, double Height) canvas)
{
// 描画イメージサイズの幅
int width = l.Width + r.Image.PixelWidth;
// 描画イメージサイズの高さ
int height;
if (l.Height > r.Image.PixelHeight)
{
height = l.Height;
}
else
{
height = r.Image.PixelHeight;
}

var wb = new WriteableBitmap(
width,
height,
r.Image.DpiX,
r.Image.DpiY,
r.Image.Format,
// 左右で違うパレットのためPNGだと正常に表示不可
r.Image.Palette);

wb.WritePixels(
new Int32Rect(0, 0, l.Width, l.Height),
l.Data,
l.Stride,
0);

wb.WritePixels(
new Int32Rect(l.Width, 0, r.Image.PixelWidth, r.Image.PixelHeight),
r.Data,
r.Stride,
0);

if (wb.CanFreeze)
{
wb.Freeze();
}

await Task.CompletedTask;
return (wb, ResultImageSize((width, height), (canvas.Width, canvas.Height)));
}
[ □ Tree ] 返信 編集キー/

▲[ 103125 ] / ▼[ 103131 ]
■103130 / 1階層)  Re[1]: パレットの異なる2枚のPNGファイルを1枚に合成して表示
□投稿者/ KOZ (444回)-(2024/05/26(Sun) 18:21:27)
2024/05/26(Sun) 18:45:04 編集(投稿者)
No103125 (紅 さん) に返信
> しかし、PNGファイルだと左右で違うパレットのため片方の画像が正常に表示できません。

フルカラーにしてから合成すればよいのでは?

モノクロビットマップとフルカラービットマップでテストしました。

static void Main(string[] args) {
    var left = new BitmapImage(new Uri(@"z:\mono.bmp"));
    var right = new BitmapImage(new Uri(@"z:\fullcolor.bmp"));
    var writeableBitmap = Combine(left, right);
    using (FileStream fileStream = new FileStream(@"z:\003.png", FileMode.Create)) {
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
        encoder.Save(fileStream);
    }
}

public static WriteableBitmap Combine(BitmapImage left, BitmapImage right) {
    var width = left.PixelWidth + right.PixelWidth;
    var height = Math.Max(left.PixelHeight, right.PixelHeight);
    var wb = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
    WriteImage(wb, left, 0, 0);
    WriteImage(wb, right, left.PixelWidth, 0);
    if (wb.CanFreeze) wb.Freeze();
    return wb;
}

static void WriteImage(WriteableBitmap destination, BitmapImage source, int x, int y) {
    source = ConvertPixelFormat(source, destination.Format);
    int stride = (source.PixelWidth * source.Format.BitsPerPixel + 7) / 8;
    byte[] pixels = new byte[source.PixelHeight * stride];
    source.CopyPixels(new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight), pixels, stride, 0);
    destination.WritePixels(new Int32Rect(x, y, source.PixelWidth, source.PixelHeight), pixels, stride, 0);
}

static BitmapImage ConvertPixelFormat(BitmapImage source, PixelFormat pixelFormat) {
    if (source.Format == pixelFormat) {
        return source;
    }
    var formatter = new FormatConvertedBitmap();
    formatter.BeginInit();
    formatter.Source = source;
    formatter.DestinationFormat = pixelFormat;
    formatter.EndInit();
   
    using (MemoryStream memoryStream = new MemoryStream()) {
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(formatter));
        encoder.Save(memoryStream);
        memoryStream.Seek(0, SeekOrigin.Begin);

        BitmapImage converted = new BitmapImage();
        converted.BeginInit();
        converted.StreamSource = memoryStream;
        converted.CacheOption = BitmapCacheOption.OnLoad;
        converted.EndInit();

        return converted;
    }
}

[ 親 103125 / □ Tree ] 返信 編集キー/

▲[ 103130 ] / 返信無し
■103131 / 2階層)  Re[2]: パレットの異なる2枚のPNGファイルを1枚に合成して表示
□投稿者/ 紅 (6回)-(2024/05/26(Sun) 20:04:01)
ご回答ありがとうございます。
サンプルまでご提供頂き感謝します。
おかげでPNGファイルの合成がうまくいきました。

解決済み
[ 親 103125 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -