1

ImgView32 のレイヤーに点線を描画します。後で、各レイヤーを透過 PNG として保存したいと思います。私が持っている他のレイヤーでは、保存は問題なく機能します。ただし、描画レイヤーの場合はそうではありません。

質問を理解しやすくするために、gr32 ライブラリのコード例、より具体的には Layers の例を取り上げます。メイン メニューのオプションの 1 つは、カスタム描画レイヤーを追加することです (新しいカスタム レイヤー -> シンプル描画レイヤー)。次に、そのレイヤーを透明な PNG 画像として保存しようとすると、破損した PNG ファイルが作成されます (Paint.net や Microsoft Photo Viewer などの他の画像ビューアーで開くことはできません)。次のコードでわかるように、レイヤーの bitmap32 をビットマップとして保存しようとすると、同じことが起こります...

Bitmap32 を透過 PNG として保存するための 2 つの方法を試したので、最初の方法は次のとおりです。

procedure TMainForm.SavePNGTransparentX(bm32:TBitmap32; dest:string);
var
  Y: Integer;
  X: Integer;
  Png: TPortableNetworkGraphic32;

  function IsBlack(Color32: TColor32): Boolean;
  begin
    Result:= (TColor32Entry(Color32).B = 0) and
             (TColor32Entry(Color32).G = 0) and
             (TColor32Entry(Color32).R = 0);
  end;

  function IsWhite(Color32: TColor32): Boolean;
  begin
    Result:= (TColor32Entry(Color32).B = 255) and
             (TColor32Entry(Color32).G = 255) and
             (TColor32Entry(Color32).R = 255);
  end;

begin
    bm32.ResetAlpha;
    for Y := 0 to bm32.Height-1 do
      for X := 0 to bm32.Width-1 do
      begin
//        if IsWhite(bm32.Pixel[X, Y]) then
//          bm32.Pixel[X,Y]:=Color32(255,255,255,  0);
        if IsBlack(bm32.Pixel[X, Y]) then
          bm32.Pixel[X,Y]:=Color32(  0,  0,  0,  0);
      end;

    Png:= TPortableNetworkGraphic32.Create;
    try
      Png.Assign(bm32);
      Png.SaveToFile(dest);
    finally
      Png.Free;
    end;

end;

したがって、上記の方法は、次のように PNG がレイヤーに読み込まれている場合に機能します。

mypng := TPortableNetworkGraphic32.Create;
mypng.LoadFromStream(myStream);
B := TBitmapLayer.Create(ImgView.Layers);
with B do
   try
      mypng.AssignTo(B.Bitmap);
      ...

しかし、レイヤーの例のコードで作成したレイヤーを保存しようとするとすぐに、結果が破損します。このようにレイヤーをビットマップとして保存しようとしても(ただし、レイヤーをPNGにする必要があるため、これは私の意図ではありません):

mylay := TBitmapLayer(ImgView.Layers.Items[i]);
mylay.Bitmap.SaveToFile('C:\tmp\Layer'+IntToStr(i)+'.bmp');

同じ破損が発生します。したがって、例外などを受け取ったわけではありません...どういうわけか破損して保存されます。

GR32_PNG アプローチなど、Bitmap32 を透明な PNG として保存する他の方法も試しました。

function SaveBitmap32ToPNG (sourceBitmap: TBitmap32;transparent: Boolean;bgColor32: TColor32;filename: String;compressionLevel: TCompressionLevel = 9;interlaceMethod: TInterlaceMethod = imNone): boolean;
var  png: TPNGImage;
begin
  result := false;
  try
    png := Bitmap32ToPNG (sourceBitmap,false,transparent,WinColor(bgColor32),compressionLevel,interlaceMethod);
    try
      png.SaveToFile (filename);
      result := true;
    finally
      png.Free;
    end;
  except
    result := false;
  end;
end;

どこ

function Bitmap32ToPNG (sourceBitmap: TBitmap32;paletted, transparent: Boolean;bgColor: TColor;compressionLevel: TCompressionLevel = 9;interlaceMethod: TInterlaceMethod = imNone): TPNGImage; // TPNGObject
var
  bm: TBitmap;
  png: TPNGImage;//TPngObject;
  TRNS: TCHUNKtRNS;
  p: pngImage.PByteArray;
  x, y: Integer;
begin
  Result := nil;
  png := TPngImage.Create; // TPNGObject
  try
    bm := TBitmap.Create;
    try
      bm.Assign (sourceBitmap);        // convert data into bitmap
      // force paletted on TBitmap, transparent for the web must be 8bit
      if paletted then
        bm.PixelFormat := pf8bit;
      png.interlaceMethod := interlaceMethod;
      png.compressionLevel := compressionLevel;
      png.Assign(bm);                  // convert bitmap into PNG
                                       // this is where the access violation occurs
    finally
      FreeAndNil(bm);
    end;
    if transparent then begin
      if png.Header.ColorType in [COLOR_PALETTE] then begin
        if (png.Chunks.ItemFromClass(TChunktRNS) = nil) then png.CreateAlpha;
        TRNS := png.Chunks.ItemFromClass(TChunktRNS) as TChunktRNS;
        if Assigned(TRNS) then TRNS.TransparentColor := bgColor;
      end;
      if png.Header.ColorType in [COLOR_RGB, COLOR_GRAYSCALE] then png.CreateAlpha;
      if png.Header.ColorType in [COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA] then
      begin
        for y := 0 to png.Header.Height - 1 do begin
          p := png.AlphaScanline[y];
          for x := 0 to png.Header.Width - 1
          do p[x] := AlphaComponent(sourceBitmap.Pixel[x,y]);  // TARGB(bm.Pixel[x,y]).a;
        end;
      end;
    end;
    Result := png;
  except
    png.Free;
  end;
end;

しかし、このアプローチを使用すると、この特定のレイヤーを保存しようとすると EAccessViolation が発生します。他のレイヤー(描画レイヤーではない)の場合、このカスタム描画レイヤーを除いてプロジェクトがクラッシュしません。アクセス違反は次の行で発生します。

png.Assign(bm);

Bitmap32ToPNG 関数内

なぜそれが起こるのか、どうすればこれを防ぐことができるのか分かりますか?

編集

TPositionedLayer には何らかの理由で Bitmap32 がない可能性があるため、代わりに TBitmapLayer を使用してみました。だから私のコードは次のようになります:

// adding a BitmapLayer and setting it's onPaint event to my handler
procedure TMainForm.Mynewlayer1Click(Sender: TObject);
var
  B: TBitmapLayer;
  P: TPoint;
  W, H: Single;
begin
      B := TBitmapLayer.Create(ImgView.Layers);
      with B do
      try
        Bitmap.SetSize(100,200);
        Bitmap.DrawMode := dmBlend;

        with ImgView.GetViewportRect do
          P := ImgView.ControlToBitmap(GR32.Point((Right + Left) div 2, (Top + Bottom) div 2));

        W := Bitmap.Width * 0.5;
        H := Bitmap.Height * 0.5;

        with ImgView.Bitmap do
          Location := GR32.FloatRect(P.X - W, P.Y - H, P.X + W, P.Y + H);

        Scaled := True;
        OnMouseDown := LayerMouseDown;
        OnPaint := PaintMy3Handler;
      except
        Free;
        raise;
      end;
      Selection := B;
end;

// and the PaintHandler is as follows:
procedure TMainForm.PaintMy3Handler(Sender: TObject;Buffer: TBitmap32);
var
  Cx, Cy: Single;
  W2, H2: Single;
const
  CScale = 1 / 200;
begin

  if Sender is TBitmapLayer then
    with TBitmapLayer(Sender).GetAdjustedLocation do
    begin
      // Five black pixels, five white pixels since width of the line is 5px
      Buffer.SetStipple([clBlack32, clBlack32, clBlack32, clBlack32, clBlack32,
        clWhite32, clWhite32, clWhite32, clWhite32, clWhite32]);

      W2 := (Right - Left) * 0.5;
      H2 := (Bottom - Top) * 0.5;

      Cx := Left + W2;
      Cy := Top + H2;
      W2 := W2 * CScale;
      H2 := H2 * CScale;
      Buffer.PenColor := clRed32;

      Buffer.StippleCounter := 0;
      Buffer.MoveToF(Cx-2,Top);
      Buffer.LineToFSP(Cx-2 , Bottom);

      Buffer.StippleCounter := 0;
      Buffer.MoveToF(Cx-1,Top);
      Buffer.LineToFSP(Cx-1 , Bottom);

      Buffer.StippleCounter := 0;
      Buffer.MoveToF(Cx,Top);
      Buffer.LineToFSP(Cx , Bottom);

      Buffer.StippleCounter := 0;
      Buffer.MoveToF(Cx+1,Top);
      Buffer.LineToFSP(Cx+1 , Bottom);

      Buffer.StippleCounter := 0;
      Buffer.MoveToF(Cx+2,Top);
      Buffer.LineToFSP(Cx+2 , Bottom);
    end;
end;

デフォルトのレイヤー デモ アプリケーションを使用していることに注意してください。したがって、これは追加されたコードです。デモ コードでは何も削除も変更もしていません。そこで、新しいレイヤー (TBitmapLayer) を作成し、onPaint で描画します。最後に、そのレイヤーの内容を PNG として保存します。しかし、onPaint は実際のレイヤーではなく別の場所に描画されるようです。そうしないと、保存された画像が空である理由がわかりません。今回は結果のPNGは壊れていませんが、空です...

4

1 に答える 1

0

エラーは、例TPositionedLayerがビットマップを保持しないレイヤーを作成するという事実にあります。次のコードで行うように、このレイヤー タイプを に型キャストしてTBitmapLayer、レイヤーのビットマップ イメージを作成することを期待することはできません。

  mylay := TBitmapLayer(ImgView.Layers.Items[i]);
  mylay.Bitmap.SaveToFile('C:\tmp\Layer'+IntToStr(i)+'.bmp');

.pngそのコードは表示されていませんが、ファイルに保存するのと同様のことをしていると思います。

画面への描画に使用する例 (TPositionedLayerレイヤーあり) 。ImgView.Buffer次のように .png ファイルに保存できます。

  SavePNGTransparentX(ImgView.Buffer, 'c:\tmp\imgs\buffer.png');

しかし、それが別のレイヤー画像に対して十分に機能するとは思いません。

以前のように使用しない理由は何TBitmapLayersですか?


user1137313によるコメントの後に編集

自分で見つけた解決策に触発されました(コメントを参照)。保存が必要な場合にのみ、レイヤーを追加のビットマップにペイントする次のことをお勧めします。

メニュー項目から開始

procedure TMainForm.mnFileSaveClick(Sender: TObject);
begin
  SaveLayerToPng(ImgView.Layers[ImgView.Layers.Count-1], 'c:\tmp\imgs\buffer.png');
end;

SaveLayerToPng()複数のレイヤーを同時に保存し、必要に応じてファイル名を変更する場合は、ループで呼び出したいと思うかもしれません。

それではおSaveLayerToPng()手続き

procedure TMainForm.SaveLayerToPng(L: TCustomLayer; FileName: string);
var
  bm32: TBitmap32;
begin
  bm32:= TBitmap32.Create;
  try
    bm32.SetSizeFrom(ImgView.Buffer);
    PaintSimpleDrawingHandler(L, bm32);
    SavePNGTransparentX(bm32, FileName);
  finally
    bm32.Free;
  end;
end;

ペイントする既存のPaintSimpleDrawingHandler(Sender: TObject; buffer: TBitmap32)プロシージャを呼び出してbm32から、実際の保存のために `SavePNGTransparentX() に渡します。

Graphics32例のペイント ハンドラーを使用しましたが、それPaintMy3Handler()も同様に使用できます。

最終結果はソリューションと同じTBitmap32ですが、ファイルを保存するときにのみ余分なものが描画されます。

于 2015-04-18T08:20:39.580 に答える