Конференция "Media" » Градиент с углом [D6, WinXP]
 
  • homm © (15.09.07 00:54) [20]
    > [16] Lacmus ©   (14.09.07 23:58)

    Работает только для углов до 90°?
  • Lacmus © (15.09.07 09:44) [21]
    >homm ©   (15.09.07 00:53) [19]

    Классно, хотя почему вычисляется тангенс 90 градусов без ошибки остается загадкой

    >homm ©   (15.09.07 00:54) [20]

    Там и с ClipRect тоже есть проблемы - он не выставлен
  • Lacmus © (15.09.07 17:05) [22]
    На основе homm ©   (15.09.07 00:53) [19]



    procedure DrawAngleGradient(aCanvas: TCanvas; aRect: TRect; aColor1, aColor2: TColor; aAngle: Integer);
    type
     RGBQuadArray = array[0..0] of TRGBQuad;
    var
     FromR, FromG, FromB: Byte;
     bBottomToTop: Boolean;
     Bitmap: TBitmap;
     i, j, k, W, H: Integer;
     CoeffR, CoeffG, CoeffB, SinA, CosA, C: Extended;
     Line: ^RGBQuadArray;
    begin
     W := aRect.Right - aRect.Left;
     H := aRect.Bottom - aRect.Top;
     Bitmap := TBitmap.Create;
     try
       Bitmap.HandleType  := bmDIB;
       Bitmap.PixelFormat := pf32bit;
       Bitmap.SetSize(W, H);

       aAngle := aAngle mod 360;

       if aAngle < 0 then
         aAngle := aAngle + 360;

       if (aAngle >= 180) then begin
         i := aColor1;
         aColor1 := aColor2;
         aColor2 := i;
         aAngle := aAngle - 180;
       end;

       bBottomToTop := (aAngle > 90) and (aAngle < 180);
       if bBottomToTop then
         aAngle := 180 - aAngle;

       SinCos(aAngle * PI / 180, SinA, CosA);

       C := W * SinA + H * CosA;

       FromR := GetRValue(AColor1);
       FromG := GetGValue(AColor1);
       FromB := GetBValue(AColor1);

       CoeffR := (GetRValue(aColor2) - FromR) / C;
       CoeffG := (GetGValue(aColor2) - FromG) / C;
       CoeffB := (GetBValue(aColor2) - FromB) / C;

       for i := 0 to H - 1 do begin
         Line := Bitmap.ScanLine[i];
         if bBottomToTop then
           k := H - i
         else
           k := i;
         for j := 0 to W - 1 do begin
           C := j * SinA + k * CosA;
           Line[j].rgbRed   := FromR + Round(C * CoeffR);
           Line[j].rgbGreen := FromG + Round(C * CoeffG);
           Line[j].rgbBlue  := FromB + Round(C * CoeffB);
         end
       end;
       BitBlt(aCanvas.Handle, aRect.Left, aRect.Top, W, H, Bitmap.Canvas.Handle, 0, 0, SRCCOPY)
     finally
       Bitmap.Free
     end
    end;


  • homm © (15.09.07 22:28) [23]
    Спасибо :) все-же я сам видимо не так силен в геметрии (или в логике, раз не смог додуматься до более простого варианта).
    Но я пошел еще дальше ;)

    procedure DrawAngleGradient(aCanvas: TCanvas; aRect: TRect; aColor1, aColor2: TColor; aAngle: Integer);
    type
     RGBQuadArray = array[0..0] of TRGBQuad;
    var
     FromR, FromG, FromB: Byte;
     bBottomToTop: Boolean;
     Bitmap: TBitmap;
     i, j, k, W, H: Integer;
     CoeffR, CoeffG, CoeffB, SinA, CosA, C: Integer;
     Line: ^RGBQuadArray;
    begin
     W := aRect.Right - aRect.Left;
     H := aRect.Bottom - aRect.Top;
     Bitmap := TBitmap.Create;
     try
       Bitmap.HandleType  := bmDIB;
       Bitmap.PixelFormat := pf32bit;
       Bitmap.Width := W;
       Bitmap.Height := H;

       aAngle := aAngle mod 360;

       if aAngle < 0 then
         aAngle := aAngle + 360;

       if (aAngle >= 180) then begin
         i := aColor1;
         aColor1 := aColor2;
         aColor2 := i;
         aAngle := aAngle - 180;
       end;

       bBottomToTop := (aAngle > 90) and (aAngle < 180);
       if bBottomToTop then
         aAngle := 180 - aAngle;
         
       SinA := round(sin(aAngle * PI / 180)*256);
       CosA := round(cos(aAngle * PI / 180)*256);

       C := W * SinA + H * CosA;

       FromR := GetRValue(AColor1);
       FromG := GetGValue(AColor1);
       FromB := GetBValue(AColor1);

       CoeffR := (GetRValue(aColor2) - FromR)*256 div (C shr 8);
       CoeffG := (GetGValue(aColor2) - FromG)*256 div (C shr 8);
       CoeffB := (GetBValue(aColor2) - FromB)*256 div (C shr 8);

       for i := 0 to H - 1 do begin
         Line := Bitmap.ScanLine[i];
         if bBottomToTop then
           k := H - i
         else
           k := i;
         for j := 0 to W - 1 do begin
           C := j * SinA + k * CosA;
           Line[j].rgbRed   := FromR + ((C * CoeffR) shr 16);
           Line[j].rgbGreen := FromG + ((C * CoeffG) shr 16);
           Line[j].rgbBlue  := FromB + ((C * CoeffB) shr 16);
         end;
       end;

       BitBlt(aCanvas.Handle, aRect.Left, aRect.Top, W, H, Bitmap.Canvas.Handle, 0, 0, SRCCOPY)
     finally
       Bitmap.Free
     end;
    end;



    мой старый вариант на тестовой сцене — 2050мс,
    вариант из [22] — 1150мс,
    этот ваиант — 310мс.
  • antonn © (15.09.07 22:43) [24]
    homm ©  
    k * CosA;

    можно вынести в первый цикл, будет еще чуть быстрее:)
  • homm © (15.09.07 22:55) [25]
    > [24] antonn ©   (15.09.07 22:43)

    Даже нескольких процентов не получилось :) Тем не менее точность повысил точность.

    procedure DrawAngleGradient(aCanvas: TCanvas; aRect: TRect; aColor1, aColor2: TColor; aAngle: Integer);
    type
     RGBQuadArray = array[0..0] of TRGBQuad;
    var
     FromR, FromG, FromB: Byte;
     bBottomToTop: Boolean;
     Bitmap: TBitmap;
     i, j, k, W, H: Integer;
     CoeffR, CoeffG, CoeffB, SinA, CosA, C, C1: Integer;
     Line: ^RGBQuadArray;
    begin
     W := aRect.Right - aRect.Left;
     H := aRect.Bottom - aRect.Top;
     Bitmap := TBitmap.Create;
     try
       Bitmap.HandleType  := bmDIB;
       Bitmap.PixelFormat := pf32bit;
       Bitmap.Width := W;
       Bitmap.Height := H;

       aAngle := aAngle mod 360;

       if aAngle < 0 then
         aAngle := aAngle + 360;

       if (aAngle >= 180) then begin
         i := aColor1;
         aColor1 := aColor2;
         aColor2 := i;
         aAngle := aAngle - 180;
       end;

       bBottomToTop := (aAngle > 90) and (aAngle < 180);
       if bBottomToTop then
         aAngle := 180 - aAngle;
         
       SinA := round(sin(aAngle * PI / 180)*4096);
       CosA := round(cos(aAngle * PI / 180)*4096);

       C := (W * SinA + H * CosA) shr 12;

       FromR := GetRValue(AColor1);
       FromG := GetGValue(AColor1);
       FromB := GetBValue(AColor1);

       CoeffR := (GetRValue(aColor2) - FromR)*4096 div C;
       CoeffG := (GetGValue(aColor2) - FromG)*4096 div C;
       CoeffB := (GetBValue(aColor2) - FromB)*4096 div C;

       for i := 0 to H - 1 do begin
         Line := Bitmap.ScanLine[i];
         if bBottomToTop then
           k := (H - i) * CosA
         else
           k := i * CosA;
         for j := 0 to W - 1 do begin
           C := j * SinA + k;
           Line[j].rgbRed   := FromR + ((C * CoeffR) shr 24);
           Line[j].rgbGreen := FromG + ((C * CoeffG) shr 24);
           Line[j].rgbBlue  := FromB + ((C * CoeffB) shr 24);
         end;
       end;

       BitBlt(aCanvas.Handle, aRect.Left, aRect.Top, W, H, Bitmap.Canvas.Handle, 0, 0, SRCCOPY)
     finally
       Bitmap.Free
     end;
    end;

  • DVM © (15.01.08 14:21) [26]

    > homm ©   (15.09.07 22:55) [25]

    Вот так побыстрее процентов на 20:


    procedure DrawAngleGradient2(DC: HDC; ARect: TRect; AColor1, AColor2: TColor; AAngle: Integer);
    type
    RGBQuadArray = array[0..0] of TRGBQuad;
    var
    FromR, FromG, FromB: Byte;
    bBottomToTop: Boolean;
    i, j, k, W, H: Integer;
    CoeffR, CoeffG, CoeffB, SinA, CosA, C: Integer;
    Line: ^RGBQuadArray;

    bmi: BITMAPINFO;
    PBits: pointer;
    MemDC: HDC;
    MemBmp: HBITMAP;

    LineBytes: integer;
    nRow: integer;

    function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
    begin
     Dec(Alignment);
     Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
     Result := Result div 8;
    end;

    function GetScanLine(bmi: BITMAPINFO; Bits: pointer; Row: Integer): Pointer;
    var
      nRow: integer;
    begin
      if bmi.bmiHeader.biHeight > 0 then
        nRow := bmi.bmiHeader.biHeight - Row - 1
      else
        nRow := Row;
      Integer(Result) := Integer(Bits) + nRow * BytesPerScanline(bmi.bmiHeader.biWidth, bmi.bmiHeader.biBitCount, 32);
    end;

    begin
      W := aRect.Right - aRect.Left;
      H := aRect.Bottom - aRect.Top;

      ZeroMemory(@bmi, sizeof(bmi));

      bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
      bmi.bmiHeader.biCompression := BI_RGB;
      bmi.bmiHeader.biBitCount := 32;
      bmi.bmiHeader.biPlanes := 1;
      bmi.bmiHeader.biWidth := w;
      bmi.bmiHeader.biHeight := h;
      bmi.bmiHeader.biSizeImage := 0;
      bmi.bmiHeader.biClrUsed :=0;
      bmi.bmiHeader.biClrImportant:=0;

      MemDC := CreateCompatibleDC(DC);
      MemBmp := CreateDIBSection(MemDC, bmi, DIB_RGB_COLORS, pBits, 0, 0);
      SelectObject(MemDC, MemBmp);

      aAngle := aAngle mod 360;

      if aAngle < 0 then
        aAngle := aAngle + 360;

      if (aAngle >= 180) then begin
        i := aColor1;
        aColor1 := aColor2;
        aColor2 := i;
        aAngle := aAngle - 180;
      end;

      bBottomToTop := (aAngle > 90) and (aAngle < 180);
      if bBottomToTop then
        aAngle := 180 - aAngle;

      SinA := round(sin(AAngle * PI / 180) * 4096);
      CosA := round(cos(AAngle * PI / 180) * 4096);

      C := (W * SinA + H * CosA) shr 12;

      FromR := GetRValue(AColor1);
      FromG := GetGValue(AColor1);
      FromB := GetBValue(AColor1);

      CoeffR := (GetRValue(aColor2) - FromR) * 4096 div C;
      CoeffG := (GetGValue(aColor2) - FromG) * 4096 div C;
      CoeffB := (GetBValue(aColor2) - FromB) * 4096 div C;

      for i := 0 to H - 1 do begin

        Line := GetScanLine(bmi, pBits, i);

        if bBottomToTop then
          k := (H - i) * CosA
        else
          k := i * CosA;

        for j := 0 to W - 1 do begin
          C := j * SinA + k;
          Line[j].rgbRed   := FromR + ((C * CoeffR) shr 24);
          Line[j].rgbGreen := FromG + ((C * CoeffG) shr 24);
          Line[j].rgbBlue  := FromB + ((C * CoeffB) shr 24);
        end;
      end;

      BitBlt(DC, ARect.Left, ARect.Top, W, H, MemDC, 0, 0, SRCCOPY);
      DeleteObject(MemBmp);
      DeleteObject(MemDC);
    end;

    А для углов кратных 90 надо вообще по другому действовать - можно в 10-20 раз быстрее залить.

 
Конференция "Media" » Градиент с углом [D6, WinXP]
Есть новые Нет новых   [133929   +472][b:0][p:0.006]