// AARotate_Fast v1.2
//
// This unit is based on the VB code written by Lefteris Eleftheriades.
// Please visit "http://www2.cs.ucy.ac.cy/~cs06ee1/", if you want to know more
//  about the background of this unit.
//
//  v1.22 :  30 Mar 2009
//    Fixed floating point exception (occured at Transparent = true)  (ver 1.20)
//    Added a parameter : Scale    (ver 1.20)
//    Added a parameter : ApplySrcAlpha (ver 1.21)
//    Fixed bug at caculating transparency (ver 1.22)
//
//  v1.1 :  23 Mar 2009
//    Added a parameter : Transparent
//
//  v1.0 : 17 Mar 2009
//    Initial release
//
//  written by Silhwan Hyun  (hyunsh@hanafos.com)


unit AARotate_Fast;

interface

uses Windows, graphics, Math, SysUtils;


// Parameters & Result
//  SrcBitmap : The source image to be rotated.
//  Rotation :  The degree by which the image should be rotated clockwise.
//  BgColor :   The color of the background where the rotated bitmap does not overlap the
//               destination bitmap.
//  Transparent : Decides if the BgColor is treated as Transparent color.
//            It means BGColor is excluded at adding color elements and Alpha.
//  ApplySrcAlpha : This parameter is valid only if the PixelFormat of source image is
//             pf32bit.
//            Decides whether to apply source image's alpha channel(acually the "rgbReserved"
//             elements of TBitmap's pixel. We can load PNG file preserving alpha channel
//             data by use of GdipLoadImageFromFile and GdipCreateHBITMAPFromBitmap. See demo
//             project.) at calculating the value of destination image's transparency.
//  AutoBlend : This parameter is not effective if Transparent is true. (= considered as
//             false if Transparent is true)
//            Decides if the edges of the rotated image should be blended with the
//             background color defined by BgColor.
//            If false, the rgbReserved byte of each pixel will be set to the appropriate
//             alpha values so that the rotated image can be blended onto another image later
//             without a harsh edge.
//  Scale  : The size(width and height) factor of rotated image.
//            Min : 0.1 ~ Max : 10.0 (ex: 0.5 : 50%, 1.0 : 100%, 2.0 : 200%)
//            note) Limited the scaled image is not lesser than 5 pixel in height or width
//  Result :  The rotated image. Returns nil if there are errors.

function FastAARotatedBitmap(SrcBitmap : TBitmap; Rotation : double; BgColor : integer;
              Transparent, ApplySrcAlpha, AutoBlend : boolean; Scale : double) : TBitmap;

procedure DrawCircleBack(ABitmap : TBitmap; bgColor, lnColor : integer);
// Draw a circle on Bitmap - see comments in implementation
procedure DrawCircle(Bitmap: TBitmap; CenterX, CenterY, Radius,
  LineWidth, Feather: single);
// Draw a disk on Bitmap - see comments in implementation
procedure DrawDisk(Bitmap: TBitmap; CenterX, CenterY, Radius, Feather: single);

implementation

procedure DrawDisk(Bitmap: TBitmap; CenterX, CenterY, Radius, Feather: single);
// Draw a disk on Bitmap. Bitmap must be a 256 color (pf8bit) palette bitmap,
// and parts outside the disk will get palette index 0, parts inside will get
// palette index 255, and in the antialiased area (feather), the pixels will
// get values inbetween.
// ***Parameters***
// Bitmap:
//   The bitmap to draw on
// CenterX, CenterY:
//   The center of the disk (float precision). Note that [0, 0] would be the
//   center of the first pixel. To draw in the exact middle of a 100x100 bitmap,
//   use CenterX = 49.5 and CenterY = 49.5
// Radius:
//   The radius of the drawn disk in pixels (float precision)
// Feather:
//   The feather area. Use 1 pixel for a 1-pixel antialiased area. Pixel centers
//   outside 'Radius + Feather / 2' become 0, pixel centers inside 'Radius - Feather/2'
//   become 255. Using a value of 0 will yield a bilevel image.
// Copyright (c) 2003 Nils Haeck M.Sc. www.simdesign.nl
var
  x, y: integer;
  LX, RX, LY, RY: integer;
  Fact: integer;
  RPF2, RMF2: single;
  P: PByteArray;
  SqY, SqDist: single;
  sqX: array of single;
begin
  // Determine some helpful values (singles)
  RPF2 := sqr(Radius + Feather/2);
  RMF2 := sqr(Radius - Feather/2);

  // Determine bounds:
  LX := Max(floor(CenterX - RPF2), 0);
  RX := Min(ceil (CenterX + RPF2), Bitmap.Width - 1);
  LY := Max(floor(CenterY - RPF2), 0);
  RY := Min(ceil (CenterY + RPF2), Bitmap.Height - 1);

  // Optimization run: find squares of X first
  SetLength(SqX, RX - LX + 1);
  for x := LX to RX do
    SqX[x - LX] := sqr(x - CenterX);

  // Loop through Y values
  for y := LY to RY do begin
    P := Bitmap.Scanline[y];
    SqY := Sqr(y - CenterY);
    // Loop through X values
    for x := LX to RX do begin

      // determine squared distance from center for this pixel
      SqDist := SqY + SqX[x - LX];

      // inside inner circle? Most often..
      if sqdist < RMF2 then begin
        // inside the inner circle.. just give the scanline the new color
        P[x] := 255
      end else begin
        // inside outer circle?
        if sqdist < RPF2 then begin
          // We are inbetween the inner and outer bound, now mix the color
          Fact := round(((Radius - sqrt(sqdist)) * 2 / Feather) * 127.5 + 127.5);
          P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
        end else begin
          P[x] := 0;
        end;
      end;
    end;
  end;
end;

procedure DrawCircle(Bitmap: TBitmap; CenterX, CenterY, Radius,
  LineWidth, Feather: single);
// Draw a circle on Bitmap. Bitmap must be a 256 color (pf8bit) palette bitmap,
// and parts outside the circle will get palette index 0, parts inside will get
// palette index 255, and in the antialiased area (feather), the pixels will
// get values inbetween.
// ***Parameters***
// Bitmap:
//   The bitmap to draw on
// CenterX, CenterY:
//   The center of the circle (float precision). Note that [0, 0] would be the
//   center of the first pixel. To draw in the exact middle of a 100x100 bitmap,
//   use CenterX = 49.5 and CenterY = 49.5
// Radius:
//   The radius of the drawn circle in pixels (float precision)
// LineWidth
//   The line width of the drawn circle in pixels (float precision)
// Feather:
//   The feather area. Use 1 pixel for a 1-pixel antialiased area. Pixel centers
//   outside 'Radius + Feather / 2' become 0, pixel centers inside 'Radius - Feather/2'
//   become 255. Using a value of 0 will yield a bilevel image. Note that Feather
//   must be equal or smaller than LineWidth (or it will be adjusted internally)
// Copyright (c) 2003 Nils Haeck M.Sc. www.simdesign.nl
var
  x, y: integer;
  LX, RX, LY, RY: integer;
  Fact: integer;
  ROPF2, ROMF2, RIPF2, RIMF2: single;
  OutRad, InRad: single;
  P: PByteArray;
  SqY, SqDist: single;
  sqX: array of single;
begin
  // Determine some helpful values (singles)
  OutRad := Radius + LineWidth/2;
  InRad  := Radius - LineWidth/2;
  ROPF2 := sqr(OutRad + Feather/2);
  ROMF2 := sqr(OutRad - Feather/2);
  RIPF2 := sqr(InRad  + Feather/2);
  RIMF2 := sqr(InRad  - Feather/2);

  // Determine bounds:
  LX := Max(floor(CenterX - ROPF2), 0);
  RX := Min(ceil (CenterX + ROPF2), Bitmap.Width - 1);
  LY := Max(floor(CenterY - ROPF2), 0);
  RY := Min(ceil (CenterY + ROPF2), Bitmap.Height - 1);

  // Checks
  if Feather > LineWidth then Feather := LineWidth;

  // Optimization run: find squares of X first
  SetLength(SqX, RX - LX + 1);
  for x := LX to RX do
    SqX[x - LX] := sqr(x - CenterX);

  // Loop through Y values
  for y := LY to RY do begin
    P := Bitmap.Scanline[y];
    SqY := Sqr(y - CenterY);
    // Loop through X values
    for x := LX to RX do begin

      // determine squared distance from center for this pixel
      SqDist := SqY + SqX[x - LX];

      // now first check if we're completely inside (most often)
      if SqDist < RIMF2 then begin
        // We're on the disk inside everything
        P[x] := 0;
      end else begin
        // completely outside?
        if SqDist < ROPF2 then begin
          // inside outer line - feather?
          if SqDist < ROMF2 then begin
            // check if we're in inside feather area
            if SqDist < RIPF2 then begin
              // We are in the feather area of inner line, now mix the color
              Fact := round(((sqrt(sqdist) - InRad) * 2 / Feather) * 127.5 + 127.5);
              P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
            end else begin
              // on the line
              P[x] := 255;
            end;
          end else begin
            // We are in the feather area of outer line, now mix the color
            Fact := round(((OutRad - sqrt(sqdist)) * 2 / Feather) * 127.5 + 127.5);
            P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
          end;
        end else begin
          // outside everything
          P[x] := 0;
        end;

      end;
    end;
  end;
end;

procedure DrawCircleBack(ABitmap : TBitmap; bgColor, lnColor : integer);
// Create a 256-color bitmap and call the DrawCircle procedure
var
  i, y: integer;
  pal: PLogPalette;
  hpal: HPALETTE;
  ColRGB, BgrRGB: integer;
  //ACenterX, ACenterY,
  //ARadius, AFeather,
  //ALineWidth
  //: single;
begin
    // 8 bits per pixel
    ABitmap.PixelFormat := pf8bit;

    // Create a gradient palette between foreground and background color
    GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
    try
      pal.palVersion := $300;
      pal.palNumEntries := 256;
      ColRGB := ColorToRGB(bgColor);
      BgrRGB := ColorToRGB(clWhite);
      for i := 0 to 255 do begin
        pal.palPalEntry[i].peRed   := round(i / 255 * (ColRGB        AND $FF) + (255 - i) / 255 * (BgrRGB        AND $FF));
        pal.palPalEntry[i].peGreen := round(i / 255 * (ColRGB shr 8  AND $FF) + (255 - i) / 255 * (BgrRGB shr 8  AND $FF));
        pal.palPalEntry[i].peBlue  := round(i / 255 * (ColRGB shr 16 AND $FF) + (255 - i) / 255 * (BgrRGB shr 16 AND $FF));
      end;
      hpal := CreatePalette(pal^);
      if hpal <> 0 then
        ABitmap.Palette := hpal;
    finally
      FreeMem(pal);
    end;

    // Fill bitmap with background color
    for y := 0 to ABitmap.Height - 1 do
      FillChar(ABitmap.Scanline[y]^, ABitmap.Width, 0);
end;

function aar_roundup(A : Double) : longint;
begin
   if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
      aar_roundup := trunc(A + 0.0000000005)
   else
      aar_roundup := trunc(A + 1);
end;

function aar_cos(degrees : double) : double;
var
   off : double;
   idegrees : integer;
begin
   off := (degrees / 30 - round(degrees / 30));
   if (off < 0.0000001) and (off > -0.0000001) then
   begin
      idegrees := round(degrees);
      if (idegrees < 0) then
         idegrees := (360 - (-idegrees mod 360))
      else
         idegrees := (idegrees mod 360);

      case (idegrees) of
              0 : result := 1.0;
             30 : result := 0.866025403784439;
             60 : result := 0.5;
             90 : result := 0.0;
            120 : result := -0.5;
            150 : result := -0.866025403784439;
            180 : result := -1.0;
            210 : result := -0.866025403784439;
            240 : result := -0.5;
            270 : result := 0.0;
            300 : result := 0.5;
            330 : result := 0.866025403784439;
            360 : result := 1.0;
         else
            result := cos(degrees * 3.14159265358979 / 180);  // it shouldn't get here
            //result := cos(degrees * 3.141592 / 180);  // it shouldn't get here
      end;
   end else
      result := cos(degrees * 3.14159265358979 / 180);
      //result := cos(degrees * 3.141592 / 180);
end;

function aar_sin(degrees : double) : double;
begin
   result := aar_cos(degrees + 90.0);
end;

function byterange(a : double) : byte;
var
   b : integer;
begin
   b := round(a);

   if b < 0 then
      b := 0;
   if b > 255 then
      b := 255;

   result := b;
end;

function dorotate(src : HBITMAP; rotation : double; bgcolor : integer; transparent,
                  use_src_alpha, autoblend : boolean; scale : double) : HBITMAP;
const
   mx : array[0..3] of integer = (-1, 1, 1, -1);
   my : array[0..3] of integer = (-1, -1, 1, 1);
var
   indminx, indminy : integer;
   indmaxx, indmaxy : integer;
  // px, py : integer;
   px, py : double;
   pcos, psin : double;
   xres, yres : double;
   width, height : integer;

   srcbmp : Bitmap;
   srcdib : array of TRGBQUAD;
   dstdib : array of TRGBQUAD;
   srcdibbmap : TBITMAPINFO;
   ldc : HDC;
   backcolor : TRGBQUAD;
   TR, TB, TG : byte;

   XX, YY : integer;
   ix, iy : integer;
   cx, cy : integer;
   tx, ty : double;
   dx, dy : double;
   DstIndex, SrcIndex : integer;
   TopL, TopR, BotL, BotR : double;
   TopL_B, TopR_B, BotL_B, BotR_B : boolean;
   Dst_rgbRed, Dst_rgbBlue, Dst_rgbGreen : double;

   screenmode : DEVMODE;
   dstbmp : HBITMAP;
   dstdibmap : TBITMAPINFO;

   autoblend_ : boolean;
   Alpha : byte;
   RAlpha : double;
   pcos2, psin2 : double;
   InRegionRatio : double;
   A             : double;
begin

  //Calculate some index values so that values can easily be looked up
   indminx := trunc(rotation / 90) mod 4;
   indminy := (indminx + 1) mod 4;
   indmaxx := (indminx + 2) mod 4;
   indmaxy := (indminx + 3) mod 4;

  //Load the source bitmaps information
   if (GetObject(src, sizeof(srcbmp), @srcbmp) = 0) then
   begin
      result := 0;
      exit;
   end;

  //Set the rotation axis default values
   px := srcbmp.bmWidth div 2;    // pivot x
   py := srcbmp.bmHeight div 2;   // pivot y
  // px := srcbmp.bmWidth / 2;    // pivot x
  // py := srcbmp.bmHeight / 2;   // pivot y

  //Calculate the cos and sin value
   pcos := aar_cos(Rotation) / abs(scale);
   psin := aar_sin(Rotation) / abs(scale);
   pcos2 := aar_cos(Rotation) * abs(scale);
   psin2 := aar_sin(Rotation) * abs(scale);

  //Calculate the x and y offset of the rotated image (half the width and height of the rotated image)
   xres := mx[indmaxx] * px * pcos2 - my[indmaxx] * py * psin2;
   yres := mx[indmaxy] * px * psin2 + my[indmaxy] * py * pcos2;

   //Get the width and height of the rotated image
   //width := aar_roundup(xres * 2);
   A := xres * 2;
   if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
      width := trunc(A + 0.0000000005)
   else
      width := trunc(A + 1);

   //height := aar_roundup(yres * 2);
   A := yres * 2;
   if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
      height := trunc(A + 0.0000000005)
   else
      height := trunc(A + 1);

   cx := (width - srcbmp.bmWidth) div 2;
   cy := (height - srcbmp.bmHeight) div 2;

  //Create the source dib array and the destdib array
   SetLength(srcdib, srcbmp.bmWidth * srcbmp.bmHeight);
   SetLength(dstdib, width * height);

  //Load source bits into srcdib
   srcdibbmap.bmiHeader.biSize := sizeof(srcdibbmap.bmiHeader);
   srcdibbmap.bmiHeader.biWidth := srcbmp.bmWidth;
   srcdibbmap.bmiHeader.biHeight := -srcbmp.bmHeight;
   srcdibbmap.bmiHeader.biPlanes := 1;
   srcdibbmap.bmiHeader.biBitCount := 32;
   srcdibbmap.bmiHeader.biCompression := BI_RGB;

   ldc := CreateCompatibleDC(0);
   GetDIBits(ldc, src, 0, srcbmp.bmHeight, srcdib, srcdibbmap, DIB_RGB_COLORS);
   DeleteDC(ldc);

   backcolor.rgbRed   := bgcolor and $000000FF;
   backcolor.rgbGreen := (bgcolor and $0000FF00) div $00000100;
   backcolor.rgbBlue  := (bgcolor and $00FF0000) div $00010000;
   TR := backcolor.rgbRed;
   TG := backcolor.rgbGreen;
   TB := backcolor.rgbBlue;
   // Surpress AutoBlend option if transparent is true;
   if transparent then autoblend_ := false else autoblend_ := autoblend;

   for XX := -cx to (width - cx - 1) do      // for destination's width
   begin
     for YY := -cy to (height - cy - 1) do   // for destination's height
     begin
      // Get the rotation translation (gives the SourceImage coordinate for each DestImage x,y)
      tx := (XX - px) * PCos - (YY - py) * pSin + px;
      ty := (XX - px) * pSin + (YY - py) * PCos + py;

      // Get nearest to the left pixel
      if (tx > -1) and (tx < 0) then    // Consider just outer border
         ix := -1
      else
         ix := trunc(tx);
      if (ty > -1) and (ty < 0) then    // Consider just outer border
         iy := -1
      else
         iy := trunc(ty);

      // Get the digits after the decimal point
      dx := Abs(tx - ix);
      dy := Abs(ty - iy);

      DstIndex := XX + cx + (YY + cy) * Width;
      SrcIndex := ix + iy * srcbmp.bmWidth;

      if (tx > -1) and (ix + 1 <= srcbmp.bmWidth) and
         (ty > -1) and (iy + 1 <= srcbmp.bmHeight) then
      begin
      // The SourcePixel color maybe a combination of upto four pixels as tx and ty
      //  are not integers.
      // The intersepted (by the current calculated source pixel) area each pixel
      //  involved (see .doc for more info)
        TopL := (1 - dx) * (1 - dy);
        TopR := dx * (1 - dy);
        BotL := (1 - dx) * dy;
        BotR := dx * dy;
      // The sum of (TopL + TopR + BotL + BotR) is always 1

        if (tx >= 0) and (ix + 1 < srcbmp.bmWidth) and
           (ty >= 0) and (iy + 1 < srcbmp.bmHeight) then
        begin
         // All the intersepted areas are placed within srcbmp region
         // Antialiasing: DestColor = SourceTopLeftPixel * TopLeftAreaIntersectedBySourcePixel
         //                         + SourceTopRightPixel * TopRightAreaIntersectedBySourcePixel
         //                         + bottomleft... + bottomrigth...
         if not transparent then
         begin
           dstdib[DstIndex].rgbRed := byterange(srcdib[SrcIndex].rgbRed * TopL
                                     + srcdib[SrcIndex+1].rgbRed * TopR
                                     + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL
                                     + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR);
           dstdib[DstIndex].rgbBlue := byterange(srcdib[SrcIndex].rgbBlue * TopL
                                     + srcdib[SrcIndex+1].rgbBlue * TopR
                                     + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL
                                     + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR);
           dstdib[DstIndex].rgbGreen := byterange(srcdib[SrcIndex].rgbGreen * TopL
                                     + srcdib[SrcIndex+1].rgbGreen * TopR
                                     + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL
                                     + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR);
           if use_src_alpha then
              dstdib[DstIndex].rgbReserved
                          := byterange(srcdib[SrcIndex].rgbReserved * TopL
                                     + srcdib[SrcIndex+1].rgbReserved * TopR
                                     + srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved * BotL
                                     + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved * BotR)
           else
              dstdib[DstIndex].rgbReserved := 255;  // alpha value for future use
         end else
         begin   // if transparent
           RAlpha := 0;
           TopL_B := true;
           TopR_B := true;
           BotL_B := true;
           BotR_B := true;
           dstdib[DstIndex].rgbRed := 0;
           dstdib[DstIndex].rgbBlue := 0;
           dstdib[DstIndex].rgbGreen := 0;

           if (srcdib[SrcIndex].rgbRed <> TR) or
              (srcdib[SrcIndex].rgbBlue <> TB) or
              (srcdib[SrcIndex].rgbGreen <> TG) then
           begin
              if use_src_alpha then
                 RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
              else
                 RAlpha := TopL;
              TopL_B := false;
              dstdib[DstIndex].rgbRed := byterange(srcdib[SrcIndex].rgbRed * TopL);
              dstdib[DstIndex].rgbBlue := byterange(srcdib[SrcIndex].rgbBlue * TopL);
              dstdib[DstIndex].rgbGreen := byterange(srcdib[SrcIndex].rgbGreen * TopL);
           end;

           if TopR <> 0 then
           if (srcdib[SrcIndex+1].rgbRed <> TR) or (srcdib[SrcIndex+1].rgbBlue <> TB) or
              (srcdib[SrcIndex+1].rgbGreen <> TG) then
           begin
              if use_src_alpha then
                 RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
              else
                 RAlpha := RAlpha + TopR;
              TopR_B := false;
              dstdib[DstIndex].rgbRed := dstdib[DstIndex].rgbRed
                                       + byterange(srcdib[SrcIndex+1].rgbRed * TopR);
              dstdib[DstIndex].rgbBlue := dstdib[DstIndex].rgbBlue
                                       + byterange(srcdib[SrcIndex+1].rgbBlue * TopR);
              dstdib[DstIndex].rgbGreen := dstdib[DstIndex].rgbGreen
                                       + byterange(srcdib[SrcIndex+1].rgbGreen * TopR);
           end;

           if BotL <> 0 then
           if (srcdib[SrcIndex+srcbmp.bmWidth].rgbRed <> TR) or
              (srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue <> TB) or
              (srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen <> TG) then
           begin
              if use_src_alpha then
                 RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
              else
                 RAlpha := RAlpha + BotL;
              BotL_B := false;
              dstdib[DstIndex].rgbRed := dstdib[DstIndex].rgbRed
                                       + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL);
              dstdib[DstIndex].rgbBlue := dstdib[DstIndex].rgbBlue
                                       + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL);
              dstdib[DstIndex].rgbGreen := dstdib[DstIndex].rgbGreen
                                       + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL);
           end;

           if BotR <> 0 then
           if (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed <> TR) or
              (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue <> TB) or
              (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen <> TG) then
           begin
              if use_src_alpha then
                 RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
              else
                 RAlpha := RAlpha + BotR;
              BotR_B := false;
              dstdib[DstIndex].rgbRed := byterange(dstdib[DstIndex].rgbRed
                                                 + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR);
              dstdib[DstIndex].rgbBlue := byterange(dstdib[DstIndex].rgbBlue
                                                 + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR);
              dstdib[DstIndex].rgbGreen := byterange(dstdib[DstIndex].rgbGreen
                                                 + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR);
           end;

           // InRegionRatio gets the ratio of the non-transparent area
           // + 0.0001 : Put to avoid floating point exception by huge number.
           InRegionRatio := 1 - (TopL * ord(TopL_B) + TopR * ord(TopR_B)
                               + BotR * ord(BotR_B) + BotL * ord(BotL_B)) + 0.0001;
           dstdib[DstIndex].rgbRed := byterange(dstdib[DstIndex].rgbRed / InRegionRatio);
           dstdib[DstIndex].rgbBlue := byterange(dstdib[DstIndex].rgbBlue / InRegionRatio);
           dstdib[DstIndex].rgbGreen := byterange(dstdib[DstIndex].rgbGreen / InRegionRatio);

           dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);
         end;
        end else
        begin
         // Some elements of the intersepted areas are placed out of srcbmp region.
         // For the intersepted areas which are placed out of srcbmp region,
         //  - Transparent is false AND AutoBlend is true : add background color
         //  - Transparent is true OR AutoBlend is false : do nothing
         // Determine the elements which are placed out of srcbmp region.
           TopL_B := false; TopR_B := false; BotL_B := false; BotR_B := false;
           if tx < 0 then begin TopL_B := true; BotL_B := true; end;
           if ty < 0 then begin TopL_B := true; TopR_B := true; end;
           if ix = (srcbmp.bmWidth - 1) then begin TopR_B := true; BotR_B := true; end;
           if iy = (srcbmp.bmHeight - 1) then begin BotL_B := true; BotR_B := true; end;

           RAlpha := 0;
           Dst_rgbRed := 0;
           Dst_rgbBlue := 0;
           Dst_rgbGreen := 0;
           
           if TopL_B then   // if Top-Left element is out of srcbmp region
           begin
             if autoblend_ then
             begin
               Dst_rgbRed := backcolor.rgbRed * TopL;
               Dst_rgbBlue := backcolor.rgbBlue * TopL;
               Dst_rgbGreen := backcolor.rgbGreen * TopL;
             end;
           end else begin   // Top-Left element is within srcbmp region
             if not transparent then
             begin
               Dst_rgbRed := srcdib[SrcIndex].rgbRed * TopL;
               Dst_rgbBlue := srcdib[SrcIndex].rgbBlue * TopL;
               Dst_rgbGreen := srcdib[SrcIndex].rgbGreen * TopL;
               if use_src_alpha then
                    RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
                 else
                    RAlpha := TopL;
             end else
             begin   // if transparent then
               if (srcdib[SrcIndex].rgbRed <> TR) or (srcdib[SrcIndex].rgbBlue <> TB) or
                  (srcdib[SrcIndex].rgbGreen <> TG) then
               begin
                 Dst_rgbRed := srcdib[SrcIndex].rgbRed * TopL;
                 Dst_rgbBlue := srcdib[SrcIndex].rgbBlue * TopL;
                 Dst_rgbGreen := srcdib[SrcIndex].rgbGreen * TopL;
                 if use_src_alpha then
                    RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
                 else
                    RAlpha := TopL;
               end;
             end;
           end;

           if TopR_B then   // if Top-Right element is out of srcbmp region
           begin
             if autoblend_ then
             begin
               Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * TopR;
               Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * TopR;
               Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * TopR;
             end;
           end else begin   // Top-Right element is within srcbmp region
             if not transparent then
             begin
               Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+1].rgbRed * TopR;
               Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+1].rgbBlue * TopR;
               Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+1].rgbGreen * TopR;
               if use_src_alpha then
                    RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
                 else
                    RAlpha := RAlpha + TopR;
             end else
             begin  //  if transparent then
               if (srcdib[SrcIndex+1].rgbRed <> TR) or (srcdib[SrcIndex+1].rgbBlue <> TB) or
                  (srcdib[SrcIndex+1].rgbGreen <> TG) then
               begin
                 Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+1].rgbRed * TopR;
                 Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+1].rgbBlue * TopR;
                 Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+1].rgbGreen * TopR;
                 if use_src_alpha then
                    RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
                 else
                    RAlpha := RAlpha + TopR;
               end;
             end;
           end;

           if BotL_B then   // if Bottom-Left element is out of srcbmp region
           begin
             if autoblend_ then
             begin
               Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * BotL;
               Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * BotL;
               Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * BotL;
             end;
           end else begin   // Bottom-Left element is within srcbmp region
             if not transparent then
             begin
               Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL;
               Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL;
               Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL;
               if use_src_alpha then
                    RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
                 else
                    RAlpha := RAlpha + BotL;
             end else
             begin  // if transparent then
               if (srcdib[SrcIndex+srcbmp.bmWidth].rgbRed <> TR) or
                  (srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue <> TB) or
                  (srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen <> TG) then
               begin
                 Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL;
                 Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL;
                 Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL;
                 if use_src_alpha then
                    RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
                 else
                    RAlpha := RAlpha + BotL;
               end;
             end;
           end;

           if BotR_B then   // if Bottom-Right element is out of srcbmp region
           begin
             if autoblend_ then
             begin
               Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * BotR;
               Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * BotR;
               Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * BotR;
             end;
           end else begin   // Bottom-Right element is within srcbmp region
             if not transparent then
             begin
               Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR;
               Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR;
               Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR;
               if use_src_alpha then
                    RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
                 else
                    RAlpha := RAlpha + BotR;
             end else
             begin  // if transparent then
               if (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed <> TR) or
                  (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue <> TB) or
                  (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen <> TG) then
               begin
                 Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR;
                 Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR;
                 Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR;
                 if use_src_alpha then
                    RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
                 else
                    RAlpha := RAlpha + BotR;
               end;
             end;
           end;

           if transparent then begin
           // InRegionRatio gets the ratio of the area within srcbmp region.
           // + 0.0001 : Put to avoid floating point exception by huge number.
              InRegionRatio := 1 - (TopL * ord(TopL_B) + TopR * ord(TopR_B)
                                  + BotR * ord(BotR_B) + BotL * ord(BotL_B)) + 0.0001;
              dstdib[DstIndex].rgbRed := byterange(Dst_rgbRed / InRegionRatio);
              dstdib[DstIndex].rgbBlue := byterange(Dst_rgbBlue / InRegionRatio);
              dstdib[DstIndex].rgbGreen := byterange(Dst_rgbGreen / InRegionRatio);
           end else begin
              dstdib[DstIndex].rgbRed := byterange(Dst_rgbRed);
              dstdib[DstIndex].rgbBlue := byterange(Dst_rgbBlue);
              dstdib[DstIndex].rgbGreen := byterange(Dst_rgbGreen); 
           end;

         // Set alpha value for future use
           if autoblend_ then
              dstdib[DstIndex].rgbReserved := 255
           else
              dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);

          { else if transparent then
           begin
              dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);
           end else begin
              Alpha := byterange((1 - (TopL * integer(TopL_B)
                                     + TopR * integer(TopR_B)
                                     + BotR * integer(BotR_B)
                                     + BotL * integer(BotL_B))) * 255);
              dstdib[DstIndex].rgbReserved := Alpha;
           end; }
        end;
      end else
      begin
      // for entirely out of srcbmp region
      // Color the destination with the background color, if (not transparent).
        if (not transparent) then
        begin
          dstdib[DstIndex].rgbRed := backcolor.rgbRed;
          dstdib[DstIndex].rgbBlue := backcolor.rgbBlue;
          dstdib[DstIndex].rgbGreen := backcolor.rgbGreen;
        end;
        dstdib[DstIndex].rgbReserved := 0;  // alpha value for future use
      end;

     end;  // for YY
   end;  // for XX

   SetLength(srcdib, 0);

  //Get Current Display Settings
   screenmode.dmSize := sizeof(DEVMODE);
   EnumDisplaySettings(nil, $FFFFFFFF{ENUM_CURRENT_SETTINGS}, screenmode);

  //Create the final bitmap object
   dstbmp := CreateBitmap(width, height, 1, screenmode.dmBitsPerPel, nil);

  //Write the bits into the bitmap and return it
   dstdibmap.bmiHeader.biSize := sizeof(dstdibmap.bmiHeader);
   dstdibmap.bmiHeader.biWidth := width;
   dstdibmap.bmiHeader.biHeight := -height;
   dstdibmap.bmiHeader.biPlanes := 1;
   dstdibmap.bmiHeader.biBitCount := 32;
   dstdibmap.bmiHeader.biCompression := BI_RGB;
   SetDIBits(0, dstbmp, 0, height, dstdib, dstdibmap, DIB_RGB_COLORS);

   SetLength(dstdib, 0);

   result := dstbmp;
end;

function FastAARotatedBitmap(SrcBitmap : TBitmap;
                             Rotation  : double;
                             BgColor   : integer;
                             Transparent,
                             ApplySrcAlpha,
                             AutoBlend : boolean;
                             Scale : double) : TBitmap;
var
   res : HBITMAP;
   mult : integer;
   //MinScale : double;
   UseAlphaChannel : boolean;
begin
   if SrcBitmap.Empty then
   begin
      result := nil;
      exit;
   end;

   if SrcBitmap.PixelFormat = pf24bit then
      UseAlphaChannel := false
   else if SrcBitmap.PixelFormat = pf32bit then
      UseAlphaChannel := ApplySrcAlpha
   else begin
      result := nil;
      exit;
   end;

   //Get rotation between (0, 360)
   mult := trunc(Rotation / 360);
   if (Rotation >= 0) then
      Rotation := Rotation - 360.0 * mult
   else
      Rotation := Rotation - 360.0 * (mult - 1);

   //Limit the scaled image is not lesser than 5 pixel in height or width
   {
   if (SrcBitmap.Height <= 5) or (SrcBitmap.Width <= 5) then
      MinScale := 1
   else if SrcBitmap.Height > SrcBitmap.Width then
      MinScale := 5 / SrcBitmap.Width
   else
      MinScale := 5 / SrcBitmap.Height;

  //Get scale between (0.1, 10.0)
   if Scale < MinScale then
      Scale := MinScale;
   if Scale < 0.1 then
      Scale := 0.1
   else if Scale > 10.0 then
      Scale := 10.0;
   }

   res := dorotate(SrcBitmap.Handle, Rotation, BgColor, Transparent, UseAlphaChannel, AutoBlend, Scale);

   if res <> 0 then
   begin
     try
        Result := TBitmap.Create;
        Result.PixelFormat := pf32bit;
        Result.Handle := res;
     except
        Result := nil;
     end;
   end else
      Result := nil;
end;


end.
