Math for slow image zoom

前端 未结 4 1732
无人及你
无人及你 2021-01-13 10:10

I have an .bmp image with a comic book layout. Currently my code works like this. If I right click and hold down mouse button i can draw a marquee type box around one of the

4条回答
  •  不要未来只要你来
    2021-01-13 10:42

    Do not use a while loop to update the zoom level, because of two problems:

    1. Different animation durations, because the speed of the zoom operation depends on the speed of the code, the current CPU usage, the CPU model, etc...
    2. Blocking of te GUI, because even when you use a delay (e.g. with Sleep), the code is running in the main thread and the program turn out being unresponsive.

    Like sarnold and Elling already said: use a timing device (e.g. a TTimer) to perform a piece of the total zoom operation on every interval. Now, there are two ways to calculate those pieces:

    1. Divide the total distance to bridge into a fixed number of small distances, set the timer's interval to the total duration divided by that number and handle the sum of all processed distances on every interval. The drawback with this method is twofold:
      • A timer's set interval is an approximation and will not be exact because of various reasons, one of them being dependend on the Windows messaging system,
      • A possible rough or unsmooth animation because of it.
    2. Recalculate the part of the distance to bridge at every interval. That way the animation will appear smooth always, whether the next interval takes two times more or not.

    I used the second solution in this answer to your related question, from which the following relevant snippets are taken:

    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; 
    

    Explanation:

    • FCropRect is the new zooming rectangle, and FPrevCropRect is the previous one,
    • FAnimRect is the rectangle in between both, depending on the progress of the animation,
    • FAnimStartTick is the time on which the zoom operation is started with a call to Zoom,
    • at every timer interval (set to 15 msec, ~67Hz refresh rate), Animate is called,
    • Done is the percentage of animation progress,
    • Invalidate triggers a repaint which draws the graphic into FAnimRect.

提交回复
热议问题