{==============================================================================

  Animation Effect
  Copyright (C) 2000-2001 by Evgeny Kryukov
  All rights reserved

  All contents of this file and all other files included in this archive
  are Copyright (C) 2001 Evgeny Kryukov. Use and/or distribution of
  them requires acceptance of the License Agreement.

  See License.txt for licence information

===============================================================================}

unit KsEffects;

{$I KsCommonVer.Inc}
{$T-,W-,X+,P+}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  KsBitmap;

type

  TKsCounter = class
  private
    FFrequency: Int64;
    FStart: Int64;
    FStop: Int64;
    FCounting: Boolean;
    FElapsedTime: Single;
  public
    constructor Create;
    procedure Start;
    function Stop: Single;
    property ElapsedTime: Single read FElapsedTime;
    property Counting: Boolean read FCounting;
  end;

procedure StartCount(var Counter: TKsCounter);
function StopCount(var Counter: TKsCounter): Single;

{ Animation }

type

  TKsAnimation = (kaNone, kaRandom, kaFade, kaSlide, kaRect);

procedure FadeAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Time: integer);

procedure SlideAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Style: integer; Time: integer);

procedure RectAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Style: integer; Time: integer);

implementation {===============================================================}

uses KsUtils;

{ Counter =====================================================================}

constructor TKsCounter.Create;
begin
  inherited Create;
  if QueryPerformanceFrequency(FFrequency) then
  begin
    FCounting := False;
    FElapsedTime := 0;
  end;
end;

procedure TKsCounter.Start;
begin
  FCounting := True;
  FElapsedTime := 0;
  QueryPerformanceCounter(FStart);
end;

function TKsCounter.Stop: Single;
begin
  QueryPerformanceCounter(FStop);
  FCounting := False;
  FElapsedTime := (FStop - FStart) / FFrequency;
  Result := FElapsedTime;
end;

procedure StartCount(var Counter: TKsCounter);
begin
  Counter := TKsCounter.Create;
  Counter.Start;
end;

function StopCount(var Counter: TKsCounter): Single;
begin
  Result := 0.0;
  if Counter <> nil then
  begin
    Result := Counter.Stop;
    Counter.Free;
    Counter := nil;
  end;
end;

{ Animation ===================================================================}

procedure FadeAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Time: integer);
var
  AniBuffer: TKsBitmap;
  Counter: TKsCounter;
  CurTime: integer;
begin
  if Time = 0 then Exit;

  AniBuffer := TKsBitmap.Create;
  try
    AniBuffer.SetSize(RectWidth(SourceRect), RectHeight(SourceRect));

    Dest.PerformTransparent(ckTransparent);
    
    CurTime := 0;
    while CurTime < Time do
    begin
      StartCount(Counter);
      try
        { Animation }

        Dest.Blend := true;
        Dest.Draw(AniBuffer, 0, 0);
        Dest.Blend := false;
        { Make Ani Frame }
        AniBuffer.SetAlpha(Round((CurTime / Time) * 240) + 15);
        AniBuffer.MergeDraw(Source, 0, 0, SourceRect);

        { Draw to screen }
        AniBuffer.Draw(DC, DestRect.Left, DestRect.Top);
      finally
        CurTime := CurTime + Round(StopCount(Counter) * 1000) + 1;
      end;
    end;

  finally
    AniBuffer.Free;
  end;
end;

procedure SlideAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Style: integer; Time: integer);
var
  AniBuffer: TKsBitmap;
  Counter: TKsCounter;
  CurTime: integer;
  AniPos: TPoint;
begin
  if Time = 0 then Exit;

  AniBuffer := TKsBitmap.Create;
  try
    AniBuffer.SetSize(RectWidth(SourceRect), RectHeight(SourceRect));

    Dest.PerformTransparent(ckTransparent);
    
    CurTime := 0;
    while CurTime < Time do
    begin
      StartCount(Counter);
      try
        { Animation }

        { Make Ani Frame }
        Source.Draw(AniBuffer, 0, 0);

        case Style of
          { From Right }
          0: AniPos := Point(RectWidth(SourceRect) - Round((CurTime / Time) * RectWidth(SourceRect)), 0);
          { From Bottom }
          1: AniPos := Point(0, RectHeight(SourceRect) - Round((CurTime / Time) * RectHeight(SourceRect)));
          { From Left }
          2: AniPos := Point(Round((CurTime / Time) * RectWidth(SourceRect)) - RectWidth(SourceRect), 0);
          { From Top }
          3: AniPos := Point(0, Round((CurTime / Time) * RectHeight(SourceRect)) - RectHeight(SourceRect));
          { From Top and Left }
          4: AniPos := Point(Round((CurTime / Time) * RectWidth(SourceRect)) - RectWidth(SourceRect), Round((CurTime / Time) * RectHeight(SourceRect)) - RectHeight(SourceRect));
          { From Top and Right }
          5: AniPos := Point(RectWidth(SourceRect) - Round((CurTime / Time) * RectWidth(SourceRect)), Round((CurTime / Time) * RectHeight(SourceRect)) - RectHeight(SourceRect));
          { From Bottom and Left }
          6: AniPos := Point(Round((CurTime / Time) * RectWidth(SourceRect)) - RectWidth(SourceRect), RectHeight(SourceRect) - Round((CurTime / Time) * RectHeight(SourceRect)));
          { From Bottom and Right }
          7: AniPos := Point(RectWidth(SourceRect) - Round((CurTime / Time) * RectWidth(SourceRect)), RectHeight(SourceRect) - Round((CurTime / Time) * RectHeight(SourceRect)));
        end;

        Dest.Blend := true;
        Dest.Draw(AniBuffer, AniPos.X, AniPos.Y);
        Dest.Blend := false;

        { Draw to screen }
        AniBuffer.Draw(DC, DestRect.Left, DestRect.Top);
      finally
        CurTime := CurTime + Round(StopCount(Counter) * 1000) + 1;
      end;
    end;

  finally
    AniBuffer.Free;
  end;
end;

procedure RectAnimation(DC: HDC; Source: TKsBitmap; SourceRect: TRect; Dest: TKsBitmap;
  DestRect: TRect; Style: integer; Time: integer);
var
  AniBuffer: TKsBitmap;
  i, j: integer;
  Counter: TKsCounter;
  CurTime: integer;
  Center: TPoint;
  ClipRect: TRect;
  Pixel: TKColor;
begin
  if Time = 0 then Exit;

  AniBuffer := TKsBitmap.Create;
  try
    AniBuffer.SetSize(RectWidth(SourceRect), RectHeight(SourceRect));

    Dest.PerformTransparent(ckTransparent);

    Center := Point(AniBuffer.Width div 2, AniBuffer.Height div 2);

    CurTime := 5;
    while CurTime < Time do
    begin
      StartCount(Counter);
      try
        { Animation }

        { Make Ani Frame }
        Source.Draw(AniBuffer, 0, 0);

        case Style of
          { From Center }
          0: ClipRect := Rect(
            Center.X - Round(Center.X * (CurTime / Time)),
            Center.Y - Round(Center.Y * (CurTime / Time)),
            Center.X + Round(Center.X * (CurTime / Time)),
            Center.Y + Round(Center.Y * (CurTime / Time))); 
          { From TopLeft }
          1: ClipRect := Rect(
            0, 0,
            Round(AniBuffer.Width * (CurTime / Time)),
            Round(AniBuffer.Height * (CurTime / Time)));
          { From TopRight }
          2: ClipRect := Rect(
            AniBuffer.Width - Round(AniBuffer.Width * (CurTime / Time)),
            0,
            AniBuffer.Width,
            Round(AniBuffer.Height * (CurTime / Time)));
          { From TopLeft }
          3: ClipRect := Rect(
            0,
            AniBuffer.Height - Round(AniBuffer.Height * (CurTime / Time)),
            Round(AniBuffer.Width * (CurTime / Time)),
            AniBuffer.Height);
          { From TopRight }
          4: ClipRect := Rect(
            AniBuffer.Width - Round(AniBuffer.Width * (CurTime / Time)),
            AniBuffer.Height - Round(AniBuffer.Height * (CurTime / Time)),
            AniBuffer.Width,
            AniBuffer.Height);
        end;

        { Draw Dest }
        Dest.Blend := true;
        Dest.Draw(AniBuffer, ClipRect.Left, ClipRect.Top, ClipRect);
        Dest.Blend := false;

        { Draw to screen }
        AniBuffer.Draw(DC, DestRect.Left, DestRect.Top);
      finally
        CurTime := CurTime + Round(StopCount(Counter) * 1000) + 1;
      end;
    end;

  finally
    AniBuffer.Free;
  end;
end;

initialization
  Randomize;
finalization
end.
