1

我正在尝试将 RotationLayer 与常规 TBitmapLayer 结合起来,以便能够尽可能地使用 ImgView32 图层。

所以我的想法是:

  1. 我有一个 TBitmapLayer(我需要它是 BitmapLayer,因为我用它做的不仅仅是旋转)。它加载了 BMP 图像
  2. 我在表单上放置了一个 TGaugeBar(就像 GR32 示例中的一样)
  3. 当鼠标按下该仪表时,我开始进行计算:我创建一个 RotationLayer,在其中放置原始 BitmapLayer.Bitmap 的内容
  4. 仪表的 OnChange,我使用在 MouseDown 中创建的 RotLayer 对象并给它一个角度,并将选择分配为 TBitmap,RotLayer.Bitmap
  5. 在 MouseUp 上,我释放了使用的临时 RotationLayer 对象

所以基本上:将图像从实际层移动到临时旋转层,在那里进行旋转,然后,完成后,将旋转后的图像移回 BitmapLayer ...

所以最后,我的逻辑似乎有效,除了我需要使用另一个 SO 问题中提供的函数手动在 BitmapLayer 上进行实际旋转(链接如下)。因为看起来rotationLayer实际上并没有旋转它的位图中的图像。它似乎只显示它旋转...

现在我的问题是:

  1. 我需要能够“调整”原始 TBitmapLayer 的大小,这样它就不会裁剪旋转的图像以适合旧的 BitmapLayer
  2. 显示rotationLayer 时,我使用BitmapCenter 将其显示在初始BitmapLayer 的顶部(我找不到另一种将其定位在我想要的位置的方法)。然而这个 BitmapCenter 似乎有两种用途:一种是定位图层,另一种是设置旋转所围绕的点。我怎样才能仍然将旋转层精确地定位在原始 BitmapLayer 的顶部,并且仍然在 Bitmap 中间有 BitmapCenter(旋转中心)?
  3. 似乎当我开始旋转时,旋转层被创建并加载了我的位图,我认为 alphaChannel 发生了一些事情,因为我认为图像变得有点暗和半透明,每次我将 BitmapLayer.Bitmap 分配给 RotationLayer .位图。我注意到,通过注释 MasterAlpha:=200 行,图像不会失去亮度,但现在当 RotationLayer 可见时,图层矩形的空白部分变为黑色。所以看起来很糟糕......而且当我执行 MouseUP 时(所以当我将旋转的位图分配给 BitmapLayer.Bitmap 时,一些黑线在旋转图像的外部仍然可见,因此在空白区域中)。关于如何保持原始图像干净的任何建议?

请帮助我解决这三个问题。

到目前为止的工作代码是:

procedure TMainForm.myrotMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  l,r,t,b:single;
  flrect:TFloatRect;
begin
    ro:=TRotlayer.Create(imgView.Layers);
    ro.Bitmap:=TBitmap32.Create;
    with ro.Bitmap do
    begin
      BeginUpdate;
      ro.Bitmap.Assign((Selection as TBitmapLayer).Bitmap);
      TLinearResampler.Create(ro.Bitmap);
      //ensure good looking edge, dynamic alternative to SetBorderTransparent
      TCustomResampler(ro.Bitmap.Resampler).PixelAccessMode := pamTransparentEdge;
      ro.BitmapCenter := FloatPoint(-(Selection as TBitmapLayer).Location.Left, -(Selection as TBitmapLayer).Location.Top);
//      MasterAlpha := 200;
      FrameRectS(BoundsRect, $FFFFFFFF);
      DrawMode := dmBlend;
      EndUpdate;
      Changed;
    end;
    ro.Scaled := True;
    (Selection as TBitmapLayer).Bitmap.Assign(ro.Bitmap);
end;

procedure TMainForm.myrotChange(Sender: TObject);
begin
  ro.Angle := myRot.Position;
  (Selection as TBitmapLayer).Bitmap.Assign(ro.Bitmap);
end;

procedure TMainForm.myrotMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    bmx:=TBitmap32.Create;
    bmx.Assign((Selection as TBitmapLayer).Bitmap);
    RotateBitmap(bmx, -(Round(ro.Angle)), false, clWhite32, true);
    (Selection as TBitmapLayer).Bitmap.Assign(bmx);
    bmx.Free;
    ro.Free;
end;

RotateBitmap 函数是从这个SO question中挑选出来的

旋转透明图像时也存在问题...使用上面的代码自行测试它们并加载一些具有透明度的PNG,您就会理解问题所在。

4

1 回答 1

0

答案(这些问题真的应该是单独的问题,但由于它们密切相关,我会处理它们。希望我不会被钉死。)

  1. 调整 TBitmapLayer 的大小以适应旋转的图像。您可以通过设置 TBitmapLayer.Location 在您的 MyRotMouseUp 过程中执行此操作。旋转位图具有正确的WidthHeight属性。有关示例,请参见下面的代码。如图所示设置位置也将其保持在ImgView

  2. 如何将其TRotationLayer精确定位在图像的顶部TBitmapLayer并在图像的中心具有旋转中心。 ATRotationLayerPosition属性一起定位。位图旋转中心与BitmapCenter属性单独设置。
    定位 aTRotLayer与定位其他层 (f.ex. TBitmapLayer) 的不同之处在于它使用Position: TFloatPointpoperty 与其他层Location: TFloatRectPosition定义图层的中心点。
    与其他图层一样,参考坐标系取决于Scaled属性。如果Scaled=False(默认)引用是对TImgView32边界的。如果您随后将较大的图像加载到 中TimgView32.Bitmap,并使用滚动条滚动图像,则TRotLayer不随图像移动。另一方面,Scaled=True如果引用是 ,TImgView32.Bitmap并且TRotLayer在滚动时跟随图像。
    要将旋转图层绑定到位图图层以便它们一起滚动,请将两个图层Scaled属性设置为True并更改您设置Location和的方式Position。我已经对代码添加了必要的修改。

  3. 在旋转层和原始位图之间来回分配位图时,图像颜色会发生变化。

是的,我清楚地看到您对原始代码的意思。这就是为什么我在评论中建议保持原始图像干净(未旋转)并跟踪角度,以便原始图像仅用于显示一次变换。仍然有一些可见的退化,但它并没有堆积到无法使用。

At myRotMouseDown():将原始图像(bmo: TBitmap32在代码中)分配给旋转层(rol: TRotationLayer)位图。bml: TBitmapLayer是隐藏的( Visible := False)。这里没有分配任何其他位图。

At myRotChange(): 改变旋转层角度,并使用相同的角度数据更新表单全局变量。这里没有分配任何其他位图。

At myRotMouseUp():原始图像被分配给位图层 ( ),并且该位图使用存储在表单中的角度bml: TBitmapLayer.Bitmap随过程旋转。RotateBitmap()bml.Location更新以适应旋转的图像。此处不分配任何其他位图(bmx: TBitmap32不需要)。bml再次可见。

我还建议不要保存旋转的图像,而只是保存角度。这样,原始图像可以保持不变,并且可以在需要时以保存的角度简单地显示。

代码(无需更改RotateBitmap(),因此此处不包括)
表单字段

  private
    bmo: TBitmap32;     // original bitmap
    bml: TBitmapLayer;
//    bmx: TBitmap32;
    rol: TRotLayer;
    roa: single;        // rotation angle

方法

procedure TForm9.FormCreate(Sender: TObject);
var
  dstr, srcr: TRect;
  png: TPortableNetworkGraphic32;
begin
  png := TPortableNetworkGraphic32.Create;
  png.LoadFromFile('c:\tmp\imgs\arr-2.png');
  bmo:= TBitmap32.Create;
  bmo.Assign(png);
//  bmo.LoadFromFile('c:\tmp\imgs\arr.bmp');
  png.Free;
  bml := TBitmapLayer.Create(ImgView.Layers);
  bml.Bitmap.SetSize(bmo.Width, bmo.Height);
  bml.Scaled := True;  // !!! Changed to True for synching with rol !!!
  //bml.Location := FloatRect(
  //  (ImgView.Width  - bml.Bitmap.Width) * 0.5,
  //  (ImgView.Height - bml.Bitmap.Height)* 0.5,
  //  (ImgView.Width  + bml.Bitmap.Width) * 0.5,
  //  (ImgView.Height + bml.Bitmap.Height)* 0.5);
  // !!! Change follows to synch scrolling of bml and rol
  bml.Location := FloatRect(
    (ImgView.Bitmap.Width  - bml.Bitmap.Width) * 0.5,
    (ImgView.Bitmap.Height - bml.Bitmap.Height)* 0.5,
    (ImgView.Bitmap.Width  + bml.Bitmap.Width) * 0.5,
    (ImgView.Bitmap.Height + bml.Bitmap.Height)* 0.5);
  dstr := Rect(0, 0, bmo.Width, bmo.Height);
  srcr := Rect(0, 0, bmo.Width, bmo.Height);
  bml.Bitmap.DrawMode := dmBlend;
  bml.Bitmap.Draw(dstr, srcr, bmo.Handle);
end;

procedure TForm9.FormDestroy(Sender: TObject);
begin
  bmo.Free;
end;

procedure TForm9.myRotChange(Sender: TObject);
begin
  rol.Angle := myRot.Position * 3.6;
  roa := rol.Angle;
//  (Selection as TBitmapLayer).Bitmap.Assign(ro.Bitmap);
end;

procedure TForm9.myRotMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  l, r, t, b: single;
  flrect: TFloatRect;
begin
  rol := TRotLayer.Create(ImgView.Layers);
  rol.Scaled := True; // !!! Added for synching with bml
  with rol.Bitmap do
  begin
    BeginUpdate;
    Assign(bmo);
    // rol.Position := FloatPoint(ImgView.Width * 0.5, ImgView.Height* 0.5);
    // !!! Change follows to synch scrolling of bml and rol
    rol.Position := FloatPoint(
      (bml.Location.Right  + bml.Location.Left)*0.5,
      (bml.Location.Bottom + bml.Location.Top)*0.5);
//    rol.Bitmap.Assign((Selection as TBitmapLayer).Bitmap);
    TLinearResampler.Create(rol.Bitmap);
    // ensure good looking edge, dynamic alternative to SetBorderTransparent
    TCustomResampler(rol.Bitmap.Resampler).PixelAccessMode := pamTransparentEdge;
//    ro.BitmapCenter := FloatPoint(-(Selection as TBitmapLayer).Location.Left,
//      -(Selection as TBitmapLayer).Location.Top);
    rol.BitmapCenter := FloatPoint(Width * 0.5, Height * 0.5);
    // MasterAlpha := 200;
    FrameRectS(BoundsRect, $FFFFFFFF);
    DrawMode := dmBlend;
    rol.Angle := roa;
    EndUpdate;
    Changed;
  end;
  bml.Visible := False;
//  (Selection as TBitmapLayer).Bitmap.Assign(ro.Bitmap);
end;

procedure TForm9.myRotMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  bml.Bitmap.Assign(bmo);
  RotateBitmap(bml.Bitmap, -(Round(roa)), True, clWhite32, True);
  //bml.Location := FloatRect(
  //  (ImgView.Width  - bml.Bitmap.Width) * 0.5,
  //  (ImgView.Height - bml.Bitmap.Height)* 0.5,
  //  (ImgView.Width  + bml.Bitmap.Width) * 0.5,
  //  (ImgView.Height + bml.Bitmap.Height)* 0.5);
  // !!! Change follows to synch scrolling of bml and rol
  bml.Location := FloatRect(
    (ImgView.Bitmap.Width  - bml.Bitmap.Width) * 0.5,
    (ImgView.Bitmap.Height - bml.Bitmap.Height)* 0.5,
    (ImgView.Bitmap.Width  + bml.Bitmap.Width) * 0.5,
    (ImgView.Bitmap.Height + bml.Bitmap.Height)* 0.5);
  bml.Bitmap.DrawMode := dmBlend;
  rol.Free;
  bml.Visible := True;
end;

最后来几张截图: 首先,没有旋转图像那么好。第二次旋转,边缘不是像素完美,但合理。

在此处输入图像描述

在此处输入图像描述

于 2015-04-23T16:42:15.460 回答