2011-11-21 17 views
5

複数の半透明のPNG画像を描画するのにGR32を使用しています。 、Delphi、GR32 + PngObject:Bitmap32への変換が期待通りに機能しない

tmp:= TBitmap32.Create; 
    LoadPNGintoBitmap32(tmp, ..., foo); 
    tmp.DrawMode:= dmBlend; 
    PaintBox321.Buffer.Draw(Rect(20, 20, 20+ tmp.Width, 20+tmp.Height), 
    tmp.ClipRect, tmp); 

第1の方法は完全に正常に動作している間:

png:= TPNGObject.Create; 
    png.LoadFromFile(...); 
    PaintBox321.Buffer.Canvas.Draw(120, 20, png); 

しかし、私はGR32のウェブサイト上で提案した方法に切り替えることを望んでいた(http://graphics32.org/wiki/FAQ/ImageFormatRelated): は、これまでのところ私は、以下の方法を使用してきました同じ結果を与えるべきである - は、アルファチャンネルで非常に奇妙な問題を引き起こします。画像を参照してください。(Paint.NETで同じ画像との比較も表示されます - 背景とアイコンの両方がエディタのレイヤーで開かれています)。イメージは、Bitmap32が正しく読み込まれていないことを示しています。任意のヒント?

Problem with TBitmap32 alpha channel

は - 私はそれが描画に関するものではありません、それはBMP32にPNGをロードすることだということがわかってきました11月22日

を追加しました。 BMP32からPNGに戻って保存すると、間違った「ホワイトニングされた」(左側にある)PNG画像が生成されます。

+1

私はまた、あなたが「= dmBlend tmp.DrawMode」に設定しました、それは「不透明度」によるものだということを前提としています。 – ComputerSaysNo

+1

@Dorin Duminica、そうではありません。彼らのウェブサイトの例では、ロードされたPNG画像の透過性があればモードがdmBlendでなければならないことが示されています。すべての画像が透明であることを知っているので、私は小切手をする必要はありません。 – migajek

答えて

9

LoadPNGintoBitmap32をロードすると、画像に透明度が2回適用され、より透明で灰色がかった外観になります(詳細は後で説明します)。

まず透明度:

これは、元のLoadPNGintoBitmap32からのコードでは、重要な部分は、コメントでマークされている:内部

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

destBitmap.Assign(PNGObject); // <--- paint to destBitmap's canvas with transparency (!) 
destBitmap.ResetAlpha;   

case PNGObject.TransparencyMode of // <--- the following code sets the transparency again for the TBitmap32 
{ ... } 

destBitmap.Assignは、以前のアプローチであなたと同じことを行います。それは、みましょうPNG画像自体をキャンバスにペイントします。この操作はPNGのアルファチャンネルを尊重します。しかし、これは必要ではありません。アルファチャンネルは2番目のステップでTBitmap32のピクセルに割り当てられるからです!次のように

今コードを変更し、重要な部分は、再びコメントが付いています

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

PNGObject.RemoveTransparency; // <--- paint PNG without any transparency... 
destBitmap.Assign(PNGObject); // <--- ...here 
destBitmap.ResetAlpha; 

srcStream.Position:=0; 
PNGObject.LoadFromStream(srcStream); // <--- read the image again to get the alpha channel back 

case PNGObject.TransparencyMode of // <--- this is ok now, the alpha channel now only exists in the TBitmap32 
{ ... } 

それは二度画像を読み取るため、上記の解決策は、非効率的です。しかし、それはあなたの2番目のアプローチがより透明なイメージを作り出す理由を示しています。

グレイシェイプについて:元のコードにもう1つ問題があります。destBitmap.Assignは、最初に背景をclWhite32で塗りつぶし、その上に画像を透過的にペイントします。そしてLoadPNGintoBitmap32が来て、その上に別の透明レイヤーを追加します。

+0

ありがとう、これは完璧な答えのファッションでなければなりません - 訂正されたコードだけでなく、何が間違っていたかの非常に詳細な説明もあります:) – migajek

+1

Hehe、あなたは歓迎です:)それは興味深い、よく提示された質問でした! –

0

PNGが誤ってTBitmap32に変換され、転送中の透過情報が失われている可能性があります。パレット化されたPNG画像の一般的なケースです。それ以外の場合は、 "Bitmap.DrawMode:= dmTransparent"と "OuterColor"を使用する必要はありません。 PNGからのtransparencry情報がTBitmpa32に正しく転送された場合、OuterColorを設定する必要はなく、DrawMode:= dmBlendが機能しました。

最も重要なことは、TBitmap32にPNGをどのようにロードしたかということです。 Vcl.ImagingからのTPngImage。(ビットマップ上に透過的に描画し、そのビットマップ上にあったものを保存したり、PNGアルファレイヤなどを使用して色を組み合わせたりすることはできますが、PNG透明度のさまざまなフォーマットを簡単に変換することはできません(パレット)をTBitmap32の各ピクセルのアルファ成分に変換します。 TPngImageがイメージを描画すると、ピクセルごとに結合RGBが得られますが、アルファコンポーネントはターゲットビットマップに転送されません。

あり、透明性とTBitmap32にPNGをロードしようと利用できるヘルパールーチンがありますが、それらには欠点がある:

(1)http://graphics32.org/wiki/FAQ/ImageFormatRelated から「LoadPNGintoBitmap32」 - それは二度透明度を適用し、そのイメージを持ちます0または255以外のアルファ値は他のソフトウェアとは異なって見えます(ガラス効果を持つ半透明の画像で最も顕著)。このコードはまずRGBにアルファを適用し、別のレイヤーとしてアルファを設定するので、ペイントするとアルファが再び適用されます。この問題の詳細については、Delphi, GR32 + PngObject: converting to Bitmap32 doesn't work as expected こちらをご覧ください。それに加えて、パレット画像からTBitmap32のアルファレイヤへの透明度を正しく変換しません。 RGBにレンダリングする前に、出力ビットマップの特定の色のピクセル(RGBにレンダリングされたピクセル)のアルファ透明度を手動で設定します。したがって、すべての白いピクセルが透明な場合、サンプルイメージの場合と同じように実際の透明度が失われます。

(2)gr32exライブラリからの "LoadBitmap32FromPNG":https://code.google.com/archive/p/gr32ex/ - (1)と同じアルゴリズムのやや異なる実装で、(1)と同じ問題があります。

ので、解決策は以下のとおりです。

  1. はTBitmap32を使用しないでください。 Vcl.Imaging.pngimage.TPngImageを使用すると、ターゲットビットマップ(スクリーンなど)に直接描画されます。これは、さまざまなPNG形式を正しく処理する最も互換性のある方法です。
  2. ヘルパールーティングを使用して、透過情報をVcl.Imaging.pngimage.TPngImageからTBitmap32に転送します。
  3. PNGをネイティブにTBitmap32にロードできるGR32 PNGライブラリを使用してください。https://sourceforge.net/projects/gr32pnglibrary/ これですべての情報が得られましたので、適切な解決策を得ることができます。
  4. 1回のパスでのアルファ層をロードする方法

ハインリヒ・ウルブリヒトは再び画像を読み取るために、その後painingと前に透明層を除去するための良い提案をしました。イメージを2回読み込むのを避けるために、PNGObject.RemoveTransparencyを呼び出す前にアルファレイヤを保存することができます。ここでは、アルファレイヤを正しく適用し、画像を一度だけロードするコードを示します。残念ながら、パレット画像では機能しません。パレット画像からTBitmap32のアルファレイヤーを正しく塗りつぶす方法が分かっている場合は、Transparent Png to TBitmap32で説明されているエフェクトを使わずにお知らせください。私はGR32を使用していない、が、私は違いが不透明によるものであると推測する。:

procedure LoadPNGintoBitmap32(DstBitmap: TBitmap32; SrcStream: TStream; out AlphaChannelUsed: Boolean); 
var 
    PNGObject: TPngImage; 
    PixelPtr: PColor32; 
    AlphaPtr: PByte; 
    SaveAlpha: PByte; 
    I, AlphaSize: Integer; 
begin 
    AlphaChannelUsed := False; 
    PNGObject := TPngImage.Create; 
    try 
    PNGObject.LoadFromStream(SrcStream); 
    AlphaPtr := PByte(PNGObject.AlphaScanline[0]); 
    if Assigned(AlphaPtr) then 
    begin 
     AlphaSize := PNGObject.Width * PNGObject.Height; 
     if AlphaSize <= 0 then raise Exception.Create('PNG files with zero dimensions are not supported to be loaded to TBitmap32'); 
     GetMem(SaveAlpha, AlphaSize); 
     try 
     Move(AlphaPtr^, SaveAlpha^, AlphaSize); 
     PNGObject.RemoveTransparency; 
     DstBitmap.Assign(PNGObject); 
     DstBitmap.ResetAlpha; 
     PixelPtr := PColor32(@DstBitmap.Bits[0]); 
     AlphaPtr := SaveAlpha; 
     for I := 0 to AlphaSize-1 do 
     begin 
      PixelPtr^ := (PixelPtr^ and $00FFFFFF) or (TColor32(AlphaPtr^) shl 24); 
      Inc(PixelPtr); 
      Inc(AlphaPtr); 
     end; 
     finally 
     FreeMem(SaveAlpha, AlphaSize); 
     end; 
     AlphaChannelUsed := True; 
    end else 
    if PNGObject.TransparencyMode = ptmNone then 
    begin 
     DstBitmap.Assign(PNGObject); 
    end else 
    begin 
     raise Exception.Create('Paletted PNG images are not supported in LoadPNGintoBitmap32, transparency cannot be stored to TBitmap32'); 
    end; 
    finally 
    FreeAndNil(PNGObject); 
    end; 
end; 
関連する問題