5

複数の半透明のPNG画像を描画するためにGR32を使用しています。これまで私は次の方法を使用してきました:

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

ただし、GR32 Webサイト( http://graphics32.org/wiki/FAQ/ImageFormatRelated)で提案されている方法に切り替えたいと思いました。

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

最初の方法は完全に正常に機能しますが、2番目の方法(同じ結果が得られるはずです)は、アルファチャネルで非常に奇妙な問題を引き起こします。画像を参照してください(Paint.NETで「配置」された同じ画像との比較も示しています)背景とアイコンの両方エディターのレイヤーで開かれました)。この画像は、Bitmap32が正しくロードまたは描画されていないことを示しています。任意のヒント?

TBitmap32アルファチャネルの問題

-11月22日追加

描画ではなく、PNGをBMP3​​2にロードすることであることがわかりました。BMP32からPNGに保存し直すと、誤った「白くされた」(左側の)PNG画像が生成されます。

4

2 に答える 2

9

その理由は、をロードしたときに画像に透明度が2回適用されLoadPNGintoBitmap32、より透明で灰色がかった外観になるためと思われます(これについては後で詳しく説明します)。

まず透明性:

これは元のコードであり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のアルファチャネルを尊重します。TBitmap32ただし、2番目のステップでアルファチャネルがのピクセルに割り当てられるため、これは必要ありません。

ここで、コードを次のように変更します。重要な部分には、コメントが付けられます。

 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回読み取るため、非効率的です。しかし、それはあなたの2番目のアプローチがより透明な画像を生成する理由を示しています。

また、灰色がかった場合:元のコードにはもう1つの問題があります。destBitmap.Assign最初に背景をで塗りつぶしclWhite32、次に画像を透明にペイントします。そして、そのLoadPNGintoBitmap32上に透明度の別のレイヤーが追加されます。

于 2011-11-21T17:07:52.347 に答える
1

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

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

PNGを透過性のあるTBitmap32にロードしようとするヘルパールーチンが利用可能ですが、欠点があります。

(1) http://graphics32.org/wiki/FAQ/ImageFormatRelatedの「LoadPNGintoBitmap32」 -透明度を2回適用するため、アルファ値が0または255以外の画像は、他のソフトウェアとは異なって見えます(半透明で最も目立ちます)ガラス効果のある画像)。このコードは、最初にアルファをRGBに適用し、次にアルファを別のレイヤーとして設定するため、ペイントすると、アルファが再度適用されます。この問題の詳細については、こちらをご覧ください:Delphi、GR32 + PngObject:Bitmap32への変換が期待どおりに機能しない 。その上、パレット画像から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にネイティブにロードできるGR32PNGライブラリを使用してくださいhttps://sourceforge.net/projects/gr32pnglibrary/ この問題に関するすべての情報が揃っているので、適切な解決策が得られる可能性があります。

アルファレイヤーを1回のパスでロードする方法

ハインリッヒ・ウルブリヒトは、痛みを感じる前に透明層を取り除き、画像をもう一度読むことを提案しました。画像が2回読み込まれないようにするには、PNGObject.RemoveTransparencyを呼び出す前にアルファレイヤーを保存します。これは、アルファレイヤーを正しく適用し、画像を1回だけロードするコードです。残念ながら、パレット画像では機能しません。パレット画像からTBitmap32のアルファレイヤーを正しく塗りつぶす方法を知っている場合は、Transparent Png to TBitmap32で説明されている効果なしで、私に知らせてください。

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;
于 2016-10-29T14:51:26.127 に答える