Вопрос по delphi, delphi-xe – Математика для медленного увеличения изображения

5

У меня есть изображение .bmp с макетом комиксов. В настоящее время мой код работает следующим образом. Если я щелкну правой кнопкой мыши и удержу кнопку мыши, я смогу нарисовать рамку типа рамки вокруг одной из рамок на странице комиксов. Когда я отпущу кнопку, это увеличит масштаб этого кадра. Но это момент. И я бы хотел, чтобы это имело эффект анимации.

Таким образом, вместо установки и установки значений PicRect на «END VALUE» quot;

PicRect.Left
PicRect.right
PicRect.top
PicRect.bottom

как видно из приведенного ниже кода, мне нужен способ, чтобы медленно туда добраться. Какой-то цикл while, который устанавливает эти значения постепенно, пока не достигнет конечного значения & quot; Но я не уверен на 100%, как работает эта математика. Ни одна из моих попыток цикла пока ничего не делает, кроме увеличения масштаба. Это процедура.

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;

Я считаю, что это можно сделать в коде выше. но также хотел добавить, что это помогает.

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;

спасибо за любую помощь в получении этого на работу.

Ваш Ответ

4   ответа
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 */

Обратите внимание, что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

Я не совсем уверен, почему вы пытаетесь получить(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 расчеты? Я должен думать, что этот код в его нынешнем виде, скорее всего, приведет к неловкому растяжению вашего контента таким образом, что, вероятно, будет раздражать пользователей и авторов обоих.

Теперь для решенияreal причина, по которой вы здесь, анимируете зум - вам, вероятно, понадобится радикально изменитьsomething, Я чувствую, что тыwhile петли, вероятно, были предназначены для масштабирования, но они приходятafter coef расчеты, поэтому я предположил, что они предназначены для чего-то другого. Но, как только вы выясните, где именно разместить цикл, чтобы вычислить разныеcoef значения в диапазоне от «без увеличения» чтобы "окончательно увеличить", вам также нужно будет добавить вызовы вrepaint на дисплее - или, в зависимости от вашей среды, может потребоваться добавлять некоторый код обратного вызова, запускаемый таймером каждые 50 мс, или что-то для перерисовки с обновленнымcoef ценности.

собираюсь принять, так как это помогло мне заставить всех работать! Glen Morse
Проблема заключалась в том, что он растягивался, с этим дополнительным циклом теперь он останавливает растяжение. X - start == y - starty работает так же, если закомментировано. Я попробую это без цикла пока посмотрим, как это работает, спасибо за это. Я не уверен, что COEF должен даже делать :( Я подумал, что цикл while заменит ту часть кода, которую вы назвали, немного сбивающей с толку. .. поскольку цикл while над ним должен был удалить растяжение Glen Morse
+1 За упоминание о таймерном решении.
Действительно, таймер - это то, что нужно использовать здесь. Использование любого типа цикла приведет к тому, что GUI будет в основном зависать на короткое время, пока выполняется анимация, тогда как таймер будет гарантировать, что GUI все равно будет реагировать на происходящие анимации.
Что будетawesome если вы добавите ответ сfixed код - кто-то другой, пытающийся сделать анимацию масштабирования изображения, вероятно, очень хотел бы знать, что конкретно нужно изменить.
0

о котором идет речь, поэтому я хотел бы уточнить, что он делает. Я оставил комментарии вне этого, потому что я быстро бросил это вместе. Большая часть кода здесь используется через открытую лицензию, которую мы нашли. Код изначально не имел циклов:

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-startx или y-starty в области просмотра. Итак, чтобы попытаться уточнить, что если у вас выделена область, которая имеет высоту 50 пикселей и ширину 100 пикселей, она будет масштабироваться и соответствовать 50 пикселям сверху вниз, заполняя область просмотра, но 100 пикселов обрезаются. Стороны падают со смотровой площадки. Таким образом, код, который был добавлен здесь, призван решить проблему, сделав меньшее из двух большим из двух. Делая это, он на самом деле будет соответствовать представлению о том, что было первоначально большим из двух, которое теперь является меньшим из двух. Это невероятно небрежное исправление, но оно работает. Ваш метод также исправит эту проблему и для нас, и может сделать это лучше. Большая проблема со всем этим состоит в том, что, если область является очень большой областью, то 200 пикселей на самом деле не может быть достаточно, чтобы исправить разницу. Для наших целей это может работать на 85% +, но не на все, поэтому этот код все еще нуждается в работе.

Другой код, который вы спрашиваете, на самом деле то, что сводило нас с ума раньше. По всему документу не хватает комментариев, и мы все еще пытаемся собрать воедино то, что все это значит. На самом деле, это сводит меня с ума. Я даже не совсем уверен, что именно он должен делать. Я попробовал раздельные и жесткие, и это на самом деле сломало. Поле зума сильно отличается от того, что предполагалось. Насколько я могу судить, текущий метод не добавляет странного растяжения, почему я не уверен.

Вот ссылка на код, который мы используем, хотя, если вы хотите взглянуть на него в полном объеме.http://www.torry.net/authorsmore.php?id=986 Zimage на этой странице.

Что касается актуального вопроса, мы не совсем понимаем, что именно Coef делает в первую очередь, поэтому внесение в него изменений приводит к тому, что мы просто ломаем вещи и не работаем методом проб и ошибок. Если вы не возражаете, взгляните на это, чтобы мы точно выяснили, что он делает, чтобы мы смогли изменить его на правильные значения и избавились от моего кода в процессе. Что позволило бы нам перейти к анимации масштабирования.

Чтобы добавить еще один вопрос об анимации. При этом, это также позволило бы нам добавить анимацию при переходе от одной точки увеличения на изображении к другой точке увеличения. Для нашего приложения это будет от одной комической панели к другой, либо снизу, либо сбоку, а в большинстве случаев и другого размера. Будет ли загрузка промежуточных значений слева, справа, сверху и снизу лучшим способом показать этот тип анимации? Если так, то я думаю, что это также поможет перейти от полного изображения к первой увеличенной панели.

5

двух проблем:

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... 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.

подобноsarnold а такжеЭллинг уже сказал: используйте устройство синхронизации (например,TTimer) выполнить часть операции полного масштабирования на каждом интервале. Теперь есть два способа рассчитать этиpieces:

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. 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.

Я использовал второе решение вэтот ответ на вашсвязанный вопрос, из которого взяты следующие соответствующие фрагменты:

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; 

Объяснение:

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.
@sarnold Wow, щедрость! Это очень, очень благородно, спасибо заранее.
Время от времени я сталкиваюсь с чем-тоso elegant это просто требует особого признания. :)
Этоsuperb ответ. Отличные детали, полезные советы по пересчету анимации при каждом такте и очень хороший пример кода. (Elegant образец кода.)
3

возможно, вполне понятным, модератором stackoverflow).

В моем предыдущем ответе было предложено посмотреть на пример кода glflow:

http://code.google.com/p/glflow/

Пример использует библиотеку GLScene и иллюстрирует как загрузку изображения, масштабирование изображения, так и анимацию.

Даже если вы не используете библиотеку GLScene, я думаю, что вы можете получить вдохновение для анимации, взглянув на пример кода.

Суть в том, что вам нужно использовать таймер для перерисовки.

Сначала разделите расстояние между начальным уровнем масштабирования и конечным уровнем масштабирования на отдельные шаги. Затем используйте таймер для циклического выполнения этих шагов и перерисовывайте каждый шаг.

+1 За упоминание о таймерном решении. Однако я не согласен с разделением расстояния на фиксированное количество интервалов таймера, потому что эти интервалы не будут точными.
@NGLN Да, я согласен. Разделение расстояния заранее является грубым подходом, и в некоторых случаях это может привести к неточностям. Я полагаю, что лучший подход к общему результату - использовать точное истекшее время для расчета позиций промежуточных кадров.

Похожие вопросы