数学用于慢速图像缩放

Gle*_*rse 5 delphi delphi-xe

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

因此,而不是将PicRect的值设置为"结束值"

PicRect.Left
PicRect.right
PicRect.top
PicRect.bottom
Run Code Online (Sandbox Code Playgroud)

如下面的代码所示,我需要一种慢慢到达那里的方法,某种类型的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;
Run Code Online (Sandbox Code Playgroud)

我相信这可以在上面的代码中完成.但也想添加这个有用的东西.

type
    TZImage = class(TGraphicControl)
  private
    FBitmap        : TBitmap;
    PicRect        : TRect;
    ShowRect       : TRect;
    FShowBorder    : boolean;
    FBorderWidth   : integer;
    FForceRepaint  : boolean;
    FMouse         : (mNone, mDrag, mZoom);
    FProportional  : boolean;
    FDblClkEnable  : boolean;
    startx, starty,
    oldx, oldy     : integer;
Run Code Online (Sandbox Code Playgroud)

感谢任何帮助让这个工作.

sar*_*old 6

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

首先,你的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 */
Run Code Online (Sandbox Code Playgroud)

请注意,x - start == y - starty案件完全被忽视了.我不知道这是否重要.

其次,这可能是在没有循环的情况下重写的.我在这里猜测,它需要一些测试,看看这是否正确,但这感觉就像正确的道路:

foo := (x - startx) - (y - starty)
if (foo > 200 || foo < -200)
    bar = foo / 200  # I assume integer truncation
    x += bar * 100
    startx += bar * 100
Run Code Online (Sandbox Code Playgroud)

我不完全确定你为什么试图达到(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;
Run Code Online (Sandbox Code Playgroud)

coef应该是从早期的覆盖?或者,你应该不是计算coefxcoefy,然后挑(大?小?更接近100000?)价值,以服务为两个.Left,.Right,.Top,和.Bottom计算?我不得不认为,这样的代码,更有可能导致内容的笨拙延伸,这种方式可能会惹恼用户和作者.

现在,为了解决你在这里的真正原因,为缩放设置动画 - 你可能需要彻底改变一些东西.我觉得你的while循环可能是为了进行缩放,但它们是coef计算之后出现的,所以我认为它们是用于其他东西的.但是,一旦你弄清楚确切地放置循环的位置来计算coef从"无缩放"到"最终缩放"范围内的不同值,你还需要添加调用以重新绘制显示 - 或者,取决于你的环境,可能需要每隔50毫秒添加一些定时器触发的回调代码,或者用更新的coef值重新绘制.


NGL*_*GLN 5

由于两个问题,请勿使用while循环更新缩放级别:

  1. 不同的动画持续时间,因为缩放操作的速度取决于代码的速度,当前的CPU使用率,CPU模型等...
  2. 阻止te GUI,因为即使你使用延迟(例如with Sleep),代码也在主线程中运行,程序没有响应.

就像sarnoldElling已经说过的那样:使用计时装置(例如a TTimer)在每个间隔上执行一次总变焦操作.现在,有两种方法来计算这些部分:

  1. 将桥接的总距离除以固定数量的小距离,将定时器的间隔设置为总持续时间除以该数量,并处理每个间隔上所有处理距离的总和.这种方法的缺点是双重的:
    • 计时器的设置间隔是近似值,由于各种原因而不准确,其中一个因素依赖于Windows消息传递系统,
    • 因此可能出现粗糙或不平滑的动画.
  2. 在每个间隔重新计算桥接距离的一部分.这样,动画将始终保持平滑,无论下一个间隔是否需要两倍.

我在回答您的相关问题时使用了第二个解决方案,从中获取以下相关摘要:

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; 
Run Code Online (Sandbox Code Playgroud)

说明:

  • FCropRect是新的缩放矩形,FPrevCropRect是前一个,
  • FAnimRect 是两者之间的矩形,取决于动画的进度,
  • FAnimStartTick是通过调用启动缩放操作的时间Zoom,
  • 在每个定时器间隔(设置为15毫秒,~67Hz刷新率),Animate被称为,
  • Done 是动画进度的百分比,
  • Invalidate触发一个将图形绘制成的重绘FAnimRect.

  • 这是一个_superb_答案.优秀的细节,每个时钟滴答重新计算动画的好建议,以及非常好的示例代码.(_Egantous_示例代码.) (3认同)
  • 我偶尔会遇到一些优雅的东西,它需要一些特别的认可.:) (2认同)