Delphi 慢图像缩放的数学方法

Delphi 慢图像缩放的数学方法,delphi,delphi-xe,Delphi,Delphi Xe,我有一个带有漫画书布局的.bmp图像。目前我的代码是这样工作的。如果我右键单击并按住鼠标按钮,我可以在漫画书页面上的一个框架周围绘制一个选框类型的框。当我松开按钮时,它将放大该帧。但它是瞬间的。我希望它有一个动画效果 因此,不必将PicRect的值设置为“结束值” 正如在下面的代码中看到的,我需要一种方法来慢慢地到达那里,某种while循环,一次设置一点这些值,直到它到达“结束值”,但我不能100%确定这个数学是如何工作的。我的任何while循环尝试都只会放大太远。这就是程序 procedure

我有一个带有漫画书布局的.bmp图像。目前我的代码是这样工作的。如果我右键单击并按住鼠标按钮,我可以在漫画书页面上的一个框架周围绘制一个选框类型的框。当我松开按钮时,它将放大该帧。但它是瞬间的。我希望它有一个动画效果

因此,不必将PicRect的值设置为“结束值”

正如在下面的代码中看到的,我需要一种方法来慢慢地到达那里,某种while循环,一次设置一点这些值,直到它到达“结束值”,但我不能100%确定这个数学是如何工作的。我的任何while循环尝试都只会放大太远。这就是程序

procedure TZImage.MouseUp(Button: TMouseButton; Shift: TShiftState;
                      X, Y: Integer);
    var coef:Double;
    t:integer;
begin
   if FMouse=mNone then Exit;
   if x>ShowRect.Right then x:=ShowRect.Right;
   if y>ShowRect.Bottom then y:=ShowRect.Bottom;
   if FMouse=mZoom then begin  //calculate new PicRect
     t:=startx;
     startx:=Min(startx,x);
     x:=Max(t,x);
     t:=starty;
     starty:=Min(starty,y);
     y:=Max(t,y);
     FMouse:=mNone;
     MouseCapture:=False;
//enable the following if you want to zoom-out by dragging in the opposite direction}
    {     if Startx>x then begin
            DblClick;
            Exit;
         end;}
         if Abs(x-startx)<5 then Exit;
         if (x - startx < y - starty) then
         begin
           while (x - startx < y - starty) do
           begin
              x := x + 100;
              startx := startx - 100;
           end;
         end
         else if (x - startx > y - starty) then
         begin
            while (x - startx > y - starty) do
            begin
                y := y + 100;
                starty := starty - 100;
            end;
         end;


    //This is were it sets the zoom info. This is were
    //I have to change to slowly get the PICRECT.Left/right/top/bottom
         if (PicRect.Right=PicRect.Left)
         then
            coef := 100000
         else
            coef:=ShowRect.Right/(PicRect.Right-PicRect.Left);
         PicRect.Left:=Round(PicRect.Left+startx/coef);
         PicRect.Right:=PicRect.Left+Round((x-startx)/coef);
         if (PicRect.Bottom=PicRect.Top)
         then
            coef := 100000
         else
            coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
         PicRect.Top:=Round(PicRect.Top+starty/coef);
         PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef);
       end;
       if FMouse=mDrag then begin
         FMouse:=mNone;
         Canvas.Pen.Mode:=pmCopy;
         Screen.Cursor:=crDefault;
       end;
       Invalidate;
    end;

谢谢你帮我把这件事做好。

我有一些建议;我不确定它们是否足以解决你的问题,但我希望它能帮助你实现这一目标

首先,您的
while
循环正在进行大量有趣的摆弄:

if (x - startx < y - starty) then
     begin
       while (x - startx < y - starty) do
       begin
          x := x + 100;
          startx := startx - 100;
       end;
     end
else if (x - startx > y - starty) then
     /* similar code */
我不完全清楚你为什么要让
(x-startx)-(y-starty)
彼此保持在200以内;也许还有更好的办法

这段代码有点让人困惑:

if (PicRect.Right=PicRect.Left)
     then
        coef := 100000
     else
        coef:=ShowRect.Right/(PicRect.Right-PicRect.Left);
     PicRect.Left:=Round(PicRect.Left+startx/coef);
     PicRect.Right:=PicRect.Left+Round((x-startx)/coef);
     if (PicRect.Bottom=PicRect.Top)
     then
        coef := 100000
     else
        coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
     PicRect.Top:=Round(PicRect.Top+starty/coef);
     PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef);
   end;
coef
是否应该从早期版本覆盖?或者,您是否应该计算
coefx
coefy
,然后选择(较大?较小?接近100000?)值,用于
.Left
.Right
.Top
.Bottom
计算?我不得不认为,从目前的情况来看,这段代码更有可能导致内容的笨拙扩展,这可能会激怒用户和作者


现在,为了解决你在这里的真正原因,动画缩放——你可能需要彻底改变一些东西。我觉得你的
,而
循环可能是用来做缩放的,但它们是在
coef
计算之后出现的,所以我认为它们是用来做其他事情的。但是,一旦您确定了在“无缩放”到“最终缩放”范围内精确放置循环以计算不同的
coef
值的位置,您还需要添加调用以重新绘制显示——或者,根据您的环境,可能需要添加一些由计时器每50毫秒触发的回调代码,或者使用更新的
coef
值重新绘制一些东西。

我实际上正在与Glen一起处理这个项目。我写了一些有问题的代码,所以我想澄清一下它的作用。我没有留下评论,因为我很快就把它拼凑起来了。这里的大部分代码都是通过我们找到的开放许可证使用的。代码最初没有循环:

if (x - startx < y - starty) then
       begin
         while (x - startx < y - starty) do
         begin
            x := x + 100;
            startx := startx - 100;
         end;
       end
       else if (x - startx > y - starty) then
       begin
          while (x - startx > y - starty) do
          begin
              y := y + 100;
              starty := starty - 100;
          end;
       end;
如果(x-startxy-starty),则
开始
而(x-startx>y-starty)呢
开始
y:=y+100;
starty:=starty-100;
结束;
结束;
这就是我添加的内容,添加它是因为原始代码没有按照我们认为的方式工作。基本上,您可以通过拖放来选择区域。选定区域会放大,但它不会显示选定的整个区域,而是将较小的x-startx或y-starty显示在视图区域中。因此,为了澄清这一点,如果您选择了一个50像素高、100像素宽的区域,它将缩放并适应从上到下填充视图区域的50像素,但100像素会被剪裁。侧面从观察区域脱落。因此,这里添加的代码是通过将两者中的较小者变为较大者来解决问题。通过这样做,它实际上将使视图与两个视图中原来较大的视图相匹配,而现在是两个视图中较小的视图。这是一个令人难以置信的草率修正,但它的工作。您的方法也可以为我们解决此问题,并且可能会以更好的方式解决此问题。最大的问题是,如果这个区域是一个非常大的区域,那么200pix可能不足以解决这个差异。出于我们的目的,它可能适用于85%+,但不是全部,因此此代码仍然需要改进

你问的另一个代码实际上是什么让我们发疯。整个文档中完全没有评论,我们仍在努力拼凑出它的确切含义。coef实际上是让我发疯的原因。我甚至不完全确定它一开始要做什么。我尝试了一个单独的coefx和coefy,这实际上打破了它。缩放框与原先设想的大不相同。据我所知,目前的方法没有增加奇怪的拉伸,至于为什么我不确定

这里有一个链接到我们正在使用的代码,如果您想全面查看它的话。在那一页上

至于手头的实际问题,我们还不清楚coef一开始到底做了什么,所以对它进行更改只会导致我们破坏一些东西,而不是以试错的方式工作。如果您不介意看一下它,让我们弄清楚它到底做了什么,这将使我们能够将它更改为正确的值,并在这个过程中删除我的slop代码。这将允许我们进入缩放动画

添加有关动画的另一个问题。在这样做时,这是否也允许我们在从图像上的一个缩放点移动到另一个缩放点时添加动画。对于我们的应用程序,它将从一个漫画板到另一个漫画板,或者在下面,或者在侧面,在大多数情况下,尺寸也不同。加载左侧、右侧、顶部和底部的中间值是否是显示该类型动画的最佳方式?如果是这样的话,我认为这在从完整图像移动到第一个缩放面板时也会起作用

我写了一篇
foo := (x - startx) - (y - starty)
if (foo > 200 || foo < -200)
    bar = foo / 200  # I assume integer truncation
    x += bar * 100
    startx += bar * 100
if (PicRect.Right=PicRect.Left)
     then
        coef := 100000
     else
        coef:=ShowRect.Right/(PicRect.Right-PicRect.Left);
     PicRect.Left:=Round(PicRect.Left+startx/coef);
     PicRect.Right:=PicRect.Left+Round((x-startx)/coef);
     if (PicRect.Bottom=PicRect.Top)
     then
        coef := 100000
     else
        coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
     PicRect.Top:=Round(PicRect.Top+starty/coef);
     PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef);
   end;
if (x - startx < y - starty) then
       begin
         while (x - startx < y - starty) do
         begin
            x := x + 100;
            startx := startx - 100;
         end;
       end
       else if (x - startx > y - starty) then
       begin
          while (x - startx > y - starty) do
          begin
              y := y + 100;
              starty := starty - 100;
          end;
       end;
procedure TZImage.Animate(Sender: TObject); 
var 
  Done: Single; 
begin 
  Done := (GetTickCount - FAnimStartTick) / FAnimDuration; 
  if Done >= 1.0 then 
  begin 
    FAnimTimer.Enabled := False; 
    FAnimRect := FCropRect; 
  end 
  else 
    with FPrevCropRect do 
      FAnimRect := Rect( 
        Left + Round(Done * (FCropRect.Left - Left)), 
        Top + Round(Done * (FCropRect.Top - Top)), 
        Right + Round(Done * (FCropRect.Right - Right)), 
        Bottom + Round(Done * (FCropRect.Bottom - Bottom))); 
  Invalidate; 
end; 

procedure TZImage.Zoom(const ACropRect: TRect); 
begin 
  FPrevCropRect := FCropRect; 
  FAnimRect := FPrevCropRect; 
  FCropRect := ACropRect; 
  FAnimStartTick := GetTickCount; 
  FAnimTimer.Enabled := True; 
end;