Конференция "Media" » Алгоритм Wu
 
  • Dmk © (12.03.16 18:14) [0]
    Привет! Может кто сталкивался с алгоритмом Wu? Есть 2 функции, но обе работают некорректно.

    Ссылка на результат работы функций:
    http://postimg.org/image/5bn15901l/

    Функция 2. В ней пикселы перекрываются.

    procedure CircleAA2(ABitmap: TScreenBitmap; dX, dY: integer; radius: integer; AColor: TColorRef; dAlpha, dOpacity: byte);
    var
     fAlpha, pAlpha: byte;
     cX, cY: integer;
     rX, rY: integer;
     rX2, rY2: integer;
     x, y: double;
     Q: integer;
     error: single;

    procedure Set4Pixels(cX, cY, deltaX, deltaY: integer; iAlpha, iOpacity: byte);
    begin
     SetPixelInRect(ABitmap, cX + deltaX, cY + deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX - deltaX, cY + deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX + deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX - deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
    end;

    begin
     cX := dX;
     cY := dY;
     rX := radius;
     rY := radius;
     rX2 := rX * rX;
     rY2 := rY * rY;

     // upper and lower halves
     Q := round(rX2 / sqrt(rX2 + rY2));

     x := 0;
     while (x <= Q) do
     begin
       y := rY * Sqrt(1 - x * x / rX2);
       error := y - floor(y);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set4Pixels(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set4Pixels(cX, cY, floor(x), floor(y) + 1, fAlpha, dOpacity);
       x := x + 1;
     end;

     // right and left halves
     Q := round(rY2 / sqrt(rX2 + rY2));

     y := 0;
     while (y <= Q) do
     begin
       x := rX * Sqrt(1 - y * y / rY2);
       error := x - floor(x);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set4Pixels(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set4Pixels(cX, cY, floor(x) + 1, floor(y), fAlpha, dOpacity);
       y := y + 1;
     end;
    end;

  • Dmk © (12.03.16 18:16) [1]
    Функция 1 - еще не доделана, но суть та же - перекрывающиеся пикселы.
    Они или заползают друг на друга или не дорисовываются.

    procedure CircleAA(ABitmap: TScreenBitmap; dX, dY: integer; radius: integer; AColor: TColorRef; dAlpha, dOpacity: byte);
    var
     iX, iY: double;
     fX, fY: double;
     x, y: integer;
     fAlpha, pAlpha: byte;
     dRadius: double;
     xyEQ: boolean;

    const
     PI: double = 3.1415926535897932384626433832795;

    begin
     iX := 0;
     iY := 0;
     x := 0;
     y := 0;

     xyEQ := False;

     //1-й квадрант, X
     SetPixelInRect(ABitmap, dX, dY - radius, AColor, dAlpha, dOpacity);
     //2-й квадрант, Y
     SetPixelInRect(ABitmap, dX + radius, dY, AColor, dAlpha, dOpacity);
     //3-й квадрант, X
     SetPixelInRect(ABitmap, dX, dY + radius, AColor, dAlpha, dOpacity);
     //4-й квадрант, Y
     SetPixelInRect(ABitmap, dX - radius, dY, AColor, dAlpha, dOpacity);

     //dRadius := radius * Cos(PI / 4);

     //Значение с Windows калькулятора более точное, чем вычисление через Cos
     dRadius := radius * 0.99990604980155050801971795294044;

     while (x <= y) do
     begin
       //Вычисление точного значения координаты Y
       iY := Sqrt(radius * radius - x * x);
       y := Floor(Int(iY)); //Целая часть Y
       fY := (iY - y); //Дробная часть Y
       fAlpha := Floor(fY * dAlpha); //Альфа дробного остатка пиксела
       pAlpha := dAlpha - fAlpha; //Альфа пиксела

       if (x > 0) and (x < y) then
       begin
         //Квадрант 1, X
         SetPixelInRect(ABitmap, dX + x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x, dY - y - 1, AColor, fAlpha, dOpacity);

         //Квадрант 2, X
         SetPixelInRect(ABitmap, dX + x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x, dY + y + 1, AColor, fAlpha, dOpacity);

         //Квадрант 3, X
         SetPixelInRect(ABitmap, dX - x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x, dY + y + 1, AColor, fAlpha, dOpacity);

         //Квадрант 4, X
         SetPixelInRect(ABitmap, dX - x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x, dY - y - 1, AColor, fAlpha, dOpacity);
       end;

       if (x = y) then
       begin
         InfoMessage(Form3.Handle, 'iY: ' + IntToStr(y)+ ' ' + 'fY: ' + FloatToStrF(fY, ffNumber, 18, 2));

         xyEQ := True;

         //Квадрант 1, X
         SetPixelInRect(ABitmap, dX + x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x, dY - y - 1, AColor, fAlpha, dOpacity);

         //Квадрант 2, X
         SetPixelInRect(ABitmap, dX + x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x, dY + y + 1, AColor, fAlpha, dOpacity);

         //Квадрант 3, X
         SetPixelInRect(ABitmap, dX - x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x, dY + y + 1, AColor, fAlpha, dOpacity);

         //Квадрант 4, X
         SetPixelInRect(ABitmap, dX - x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x, dY - y - 1, AColor, fAlpha, dOpacity);
       end;

       x := x + 1;
     end;

     x := 0;
     y := 0;

     while (y <= x) do
     begin
       //Вычисление точного значения координаты Y
       iX := Sqrt(radius * radius - y * y);
       x := Floor(Int(iX)); //Целая часть X
       fX := (iX - x); //Дробная часть X
       fAlpha := Floor(fX * dAlpha); //Альфа дробного остатка пиксела
       pAlpha := dAlpha - fAlpha; //Альфа пиксела

       if (y > 0) and (y < x) then
       begin
         //Квадрант 1, Y
         SetPixelInRect(ABitmap, dX + x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x + 1, dY - y, AColor, fAlpha, dOpacity);

         //Квадрант 2, Y
         SetPixelInRect(ABitmap, dX + x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX + x + 1, dY + y, AColor, fAlpha, dOpacity);

         //Квадрант 3, Y
         SetPixelInRect(ABitmap, dX - x, dY + y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x - 1, dY + y, AColor, fAlpha, dOpacity);

         //Квадрант 4, Y
         SetPixelInRect(ABitmap, dX - x, dY - y, AColor, pAlpha, dOpacity);
         SetPixelInRect(ABitmap, dX - x - 1, dY - y, AColor, fAlpha, dOpacity);
       end;

       y := y + 1;
     end;
    end;

  • Dmk © (12.03.16 18:18) [2]
    В некоторых случаях x=y вообще отсутствует.
    Хочется понять, в чем проблема. Не хватает точности сопроцессора?
  • Pavia © (12.03.16 18:27) [3]
    Рисуешь ты 4 сегмента. Рисовать надо по кругу. Последний пиксель сегмента не рисуешь. Начальный(нуливой) пиксель рисуешь у каждого сегмента. Так у тебя не будет дублирования и пропусков.

    Тоже самое с квадратом.
  • Dmk © (12.03.16 20:08) [4]
    Тут другая тема. Все рисуется правильно при альфа = 1. При 0.5 видно перекрывающиеся пикселы. Вот этого я и хочу избежать. Чтобы при любом диаметре и альфе был идеальный "пушистый" круг :) Даже при дробном диаметре.

    Потому что при радиусах 49 - 56 все рисуется идеально, как и должно быть. До 49 и после 56 почему то точность пропадает. Потом идеальный промежуток 189-196. Потом опять разрывы.

    Насколько я понимаю, тут 1 пиксел расползается то ли по окружности, то ли еще куда, т.к. при заданном радиусе, скажем 42, получается диаметр 85. 1 пиксел появляется из округлений до целого.
  • Dmk © (12.03.16 20:48) [5]
    Видимо X дробный надо делать, а не целочисленный.
  • Pavia © (12.03.16 20:52) [6]

    > Тут другая тема. Все рисуется правильно при альфа = 1. При
    > 0.5 видно перекрывающиеся пикселы.

    Ну так я о чём вам толкую. У вас пискели смежных сегментов перекрываются.

    Где может быть такое перекрьыие только в начале и в конце.
    Подставим


    x := 0;                                      // X=0
     while (x <= Q) do
     begin
       y := rY * Sqrt(1 - x * x / rX2);   // y=rY
       error := y - floor(y);                 // error=0
       fAlpha := Round(error * dAlpha);// fAlpha=0
       pAlpha := dAlpha - fAlpha;        // pAlpha=dAlpha=пусть 0.5
       Set4Pixels(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set4Pixels(cX, cY, floor(x), floor(y) + 1, fAlpha, dOpacity);
       x := x + 1;
     end;


    Откуда deltaX=0, deltaY=rY
    procedure Set4Pixels(cX, cY, deltaX, deltaY: integer; iAlpha, iOpacity: byte);
    begin
     SetPixelInRect(ABitmap, cX + deltaX, cY + deltaY, AColor, iAlpha, iOpacity); // cX + deltaX=cX, cY + deltaY=cY+rY
     SetPixelInRect(ABitmap, cX - deltaX, cY + deltaY, AColor, iAlpha, iOpacity); //
    cX + deltaX=cX, cY + deltaY=cY+rY
     SetPixelInRect(ABitmap, cX + deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX - deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
    end;

    Вот вам сразуже на первом шаге у вас перекрытие.
    Что-бы такого не было рисуйте все сегменты в одном направлении к примеру по часовой стрелки. Только последний пиксель в сегменте не рисуете.

    var
     fAlpha, pAlpha: byte;
     cX, cY: integer;
     rX, rY: integer;
     rX2, rY2: integer;
     x, y: double;
     Q: integer;
     error: single;

    procedure Set2Pixels1(cX, cY, deltaX, deltaY: integer; iAlpha, iOpacity: byte);
    begin
     SetPixelInRect(ABitmap, cX + deltaX, cY + deltaY, AColor, iAlpha, iOpacity);
    //Перемещаем  SetPixelInRect(ABitmap, cX - deltaX, cY + deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX + deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
    //Перемещаем   SetPixelInRect(ABitmap, cX - deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
    end;

    procedure Set2Pixels2(cX, cY, deltaX, deltaY: integer; iAlpha, iOpacity: byte);
    begin
     SetPixelInRect(ABitmap, cX - deltaX, cY + deltaY, AColor, iAlpha, iOpacity);
     SetPixelInRect(ABitmap, cX - deltaX, cY - deltaY, AColor, iAlpha, iOpacity);
    end;

    begin
     cX := dX;
     cY := dY;
     rX := radius;
     rY := radius;
     rX2 := rX * rX;
     rY2 := rY * rY;

     // upper and lower halves
     Q := round(rX2 / sqrt(rX2 + rY2));

     x := 0;
     while (x < Q) do  // Не дорисовываем последний пиксель
     begin
       y := rY * Sqrt(1 - x * x / rX2);
       error := y - floor(y);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set2Pixels1(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set2Pixels1(cX, cY, floor(x), floor(y) + 1, fAlpha, dOpacity);
       x := x + 1;
     end;

     // right and left halves
     Q := round(rY2 / sqrt(rX2 + rY2));

     y := 0;
     while (y < Q) do // Не дорисовываем последний пиксель
     begin
       x := rX * Sqrt(1 - y * y / rY2);
       error := x - floor(x);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set2Pixels1(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set2Pixels1(cX, cY, floor(x) + 1, floor(y), fAlpha, dOpacity);
       y := y + 1;
     end;

    // Рисуем оставшиеся сегменты
    // У вас они часть рисовались, по часовой другая против.
    // Так что разворачиваем оставшиеся. Что-бы все рисовались в одну сторону.
     cX := dX;
     cY := dY;
     rX := radius;
     rY := radius;
     rX2 := rX * rX;
     rY2 := rY * rY;

     // upper and lower halves
     Q := round(rX2 / sqrt(rX2 + rY2));

     x := rX;
     while (x >= 0) do // Не дорисовываем последний пиксель
     begin
       y := rY * Sqrt(1 - x * x / rX2);
       error := y - floor(y);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set2Pixels2(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set2Pixels2(cX, cY, floor(x), floor(y) + 1, fAlpha, dOpacity);
       x := x - 1;
     end;
     // right and left halves
     Q := round(rY2 / sqrt(rX2 + rY2));

     y := rY;
     while (y >= 0) do  // Не дорисовываем последний пиксель
     begin
       x := rX * Sqrt(1 - y * y / rY2);
       error := x - floor(x);
       fAlpha := Round(error * dAlpha);
       pAlpha := dAlpha - fAlpha;
       Set2Pixels2(cX, cY, floor(x), floor(y), pAlpha, dOpacity);
       Set2Pixels2(cX, cY, floor(x) + 1, floor(y), fAlpha, dOpacity);
       y := y - 1;
     end;

    end;


    Код не проверял но должен работаь
  • Dmk © (12.03.16 21:14) [7]
    >Pavia ©   (12.03.16 20:52) [6]
    Да не. Не о том речь. Центральный пиксел то не делится. Отсюда и расхождение. На рисунке видно, что пикселы не доливаются до конца. В вашем варианте получилось то же что и у меня. Я тоже дошел до этого момента и завис. Тут X дробный нужен иначе никак. Если взять Линию Ву, то там и X и Y дробные. Поэтому она получается ровненькая. Тут так же надо.
  • Dmk © (12.03.16 21:41) [8]
    Тут вот такая ерунда получается:
    http://hostingkartinok.com/show-image.php?id=d86b12c0e4e88151e8294ce3e0aad95d

    Вращение вокруг 1 пиксела прибавляет его к окружности. Отсюда и разрывы.
  • Dmk © (12.03.16 21:44) [9]
    Зная это тут надо или x,y на единицу смещать для каждого квадранта или дробный X, Y делать с шагом меньше единицы.
  • NoUser © (12.03.16 21:58) [10]
    Не хватает точности сопроцессора?
    Ага, ))   https://habrahabr.ru/company/xakep/blog/257897/


    >  //dRadius := radius * Cos(PI / 4);
    >  //Значение с Windows калькулятора более точное, чем вычисление через Cos
    >  dRadius := radius * 0.99990604980155050801971795294044;


    тут c радианами все хорошо?

    Кроме Floor и Int есть еще Trunc, Frac, Round, Ceil.
  • Dmk © (12.03.16 22:37) [11]
    NoUser ©   (12.03.16 21:58) [10]

    Спасибо, но косинус выкинут за ненадобностью.
    Заменил на Round — пока лидер среди округления :)
    Разрывы стали совсем маленькими ;)))
  • Dmk © (13.03.16 07:32) [12]
    Странно, но Cos(45) выдает полную чушь. Сто-то около 0.52, а Sin(45) около 0.85.
    sqrt(2)/2 - правильно - 0.707....
  • Pavia © (13.03.16 09:41) [13]

    > Странно, но Cos(45) выдает полную чушь. Сто-то около 0.52,
    >  а Sin(45) около 0.85.sqrt(2)/2 - правильно - 0.707....

    Если хочется работать. Ляг поспи и всё пройдёт. :-)
    Обычно в Cos и Sin  принимают на вход значения в радианах.
    Cos(45 рад.)=0.52
    Cos(45°/180°*(Pi рад))=0.707
  • Dmk © (13.03.16 10:39) [14]
    Pavia ©   (13.03.16 09:41) [13]
    Не знал, спасибо. Думал как в калькуляторе в градусах.

    Пока удалось найти величину разрыва:
    http://hostingkartinok.com/show-image.php?id=1d087e2954724b890129e4aa5bec2cc1
    Если в минус, то функция недостаточна. Если в плюс, то перекрытие.
    Не учил я математику хорошо.

    Вопрос: как привести функцию обратно в линейную величину? Т.е. действие обратное косинусу?
  • Dmk © (13.03.16 11:17) [15]
    >Вопрос: как привести функцию обратно в линейную величину? Т.е. действие обратное косинусу?
    Все, разобрался. sqrt(2).
  • NoUser © (13.03.16 14:11) [16]
    > Не учил я математику хорошо ))

    http://festival.1september.ru/articles/613708/

    Учим наизусть! Арккосинус.
  • Dmk © (13.03.16 14:17) [17]
    >Учим наизусть!
    Для чего? Кроме того я уже нашел, то что мне надо. Спасибо!
  • Dmk © (14.03.16 07:27) [18]
    Научился компенсировать альфу при превышении функции:
    http://hostingkartinok.com/show-image.php?id=edb39c0d876b66ae5604d27c028ea278

    Не пойму что делать если x=y? В цикле по X и в цикле по Y эти пикселы (x=y) встречаются дважды. Если ослабить альфу ровно в 2 раза, то получаются провалы (разрывы). Пока не понял что делать. Видимо надо как то от максимума функции считать. Максимум у меня считается так:
    cosX2 := sqrt(2);
     cosX := cosX2/2;
     maxX := cosX * radius;


    В общем вопрос остается открытым: как компенсировать альфу при x=y? Разницы функции в данной точке идет в плюс, т.е. разрыв.
  • Dmk © (14.03.16 07:28) [19]
    Вот весь код процедуры:

    procedure CircleAA(ABitmap: TScreenBitmap; dX, dY: integer; radius: integer; AColor: TColorRef; dAlpha, dOpacity: byte);
    var
     iX, iY: double;
     fX, fY: double;
     x, y: integer;
     fAlpha, pAlpha: byte;
     iR: double;
     xInc, yInc: double;
     fxInc, fyInc: double;
     maxX, maxY: double;
     difX, difY: double;
     cosX, cosY: double;
     cosX2: double;
     aP: double;

     //X - т.е. ближе к горизонтали
     procedure Set8XPixels(sX, sY, deltaX, deltaY: integer; sColor: TColorRef; pixelAlpha, fracAlpha, sOpacity: byte);
     begin
       //Квадрант 1, X
       SetPixelInRect(ABitmap, sX + deltaX, sY - deltaY, sColor, pixelAlpha, sOpacity);
       SetPixelInRect(ABitmap, sX + deltaX, sY - deltaY - 1, sColor, fracAlpha, sOpacity);
       //Квадрант 2, X
       SetPixelInRect(ABitmap, sX + deltaX, sY + deltaY, sColor, pixelAlpha, sOpacity);
       SetPixelInRect(ABitmap, sX + deltaX, sY + deltaY + 1, sColor, fracAlpha, sOpacity);
       //Квадрант 3, X
       SetPixelInRect(ABitmap, sX - deltaX, sY + deltaY, sColor, pixelAlpha, sOpacity);
       SetPixelInRect(ABitmap, sX - deltaX, sY + deltaY + 1, sColor, fracAlpha, sOpacity);
       //Квадрант 4, X
       SetPixelInRect(ABitmap, sX - deltaX, sY - deltaY, sColor, pixelAlpha, sOpacity);
       SetPixelInRect(ABitmap, sX - deltaX, sY - deltaY - 1, sColor, fracAlpha, sOpacity);
     end;

     //Y - т.е. ближе к вертикали
     procedure Set8YPixels(sX, sY, deltaX, deltaY: integer; sColor: TColorRef; pixelAlpha, fracAlpha, sOpacity: byte);
     begin
       //Квадрант 1, Y
       SetPixelInRect(ABitmap, sX + deltaX, sY - deltaY, sColor, pixelAlpha, dOpacity);
       SetPixelInRect(ABitmap, sX + deltaX + 1, sY - deltaY, sColor, fracAlpha, dOpacity);
       //Квадрант 2, Y
       SetPixelInRect(ABitmap, sX + deltaX, sY + deltaY, sColor, pixelAlpha, dOpacity);
       SetPixelInRect(ABitmap, sX + deltaX + 1, sY + deltaY, sColor, fracAlpha, dOpacity);
       //Квадрант 3, Y
       SetPixelInRect(ABitmap, sX - deltaX, sY + deltaY, sColor, pixelAlpha, dOpacity);
       SetPixelInRect(ABitmap, sX - deltaX - 1, sY + deltaY, sColor, fracAlpha, dOpacity);
       //Квадрант 4, Y
       SetPixelInRect(ABitmap, sX - deltaX, sY - deltaY, sColor, pixelAlpha, dOpacity);
       SetPixelInRect(ABitmap, sX - deltaX - 1, sY - deltaY, sColor, fracAlpha, dOpacity);
     end;

     procedure CorrectAlpha(funcDifference: double; var pixelAlpha, fracAlpha: byte);
     var
       dA: byte;

     begin
       //Перекрытие функции (-f(x)) на последнем шаге
       //Нужно ослабить альфу пиксела
       if (funcDifference < 0) then
       begin
         dA := Round((-funcDifference) * cosX2 * 100 * aP);
         if dA < pixelAlpha then
           pixelAlpha := pixelAlpha - dA else
           pixelAlpha := 0;
         if dA < fracAlpha then
           fracAlpha := fracAlpha - dA else
           fracAlpha := 0;
       end
       else
       //Разрыв функции (+f(x)) на последнем шаге
       //Нужно усилить альфу пиксела
       if (funcDifference > 0) then
       begin
         dA := Round((funcDifference) * cosX2 * 100 * aP);
         if (dA + pixelAlpha) < dAlpha then
           pixelAlpha := pixelAlpha + dA else
           pixelAlpha := dAlpha;
         if (dA + fracAlpha) < dAlpha then
           fracAlpha := fracAlpha + dA else
           fracAlpha := dAlpha;
       end;
     end;

    begin
     //Альфа для пикселов в нулевом положении
     fAlpha := Round(dAlpha * 0.5);
     //1-й квадрант, X
     SetPixelInRect(ABitmap, dX, dY - radius, AColor, fAlpha, dOpacity);
     SetPixelInRect(ABitmap, dX, dY - radius + 1, AColor, fAlpha, dOpacity);
     //2-й квадрант, Y
     SetPixelInRect(ABitmap, dX + radius, dY, AColor, fAlpha, dOpacity);
     SetPixelInRect(ABitmap, dX + radius - 1, dY, AColor, fAlpha, dOpacity);
     //3-й квадрант, X
     SetPixelInRect(ABitmap, dX, dY + radius, AColor, fAlpha, dOpacity);
     SetPixelInRect(ABitmap, dX, dY + radius - 1, AColor, fAlpha, dOpacity);
     //4-й квадрант, Y
     SetPixelInRect(ABitmap, dX - radius, dY, AColor, fAlpha, dOpacity);
     SetPixelInRect(ABitmap, dX - radius + 1, dY, AColor, fAlpha, dOpacity);

     //1% альфы
     aP := dAlpha / 100;

     //Вычтем пол пиксела из радиуса
     //чтобы компенсировать логический
     //центральный пиксел
     iR := radius - 0.5;

     cosX2 := sqrt(2);
     cosX := cosX2/2;
     cosY := cosX;
     maxX := cosX * iR;
     maxY := maxX;

     //Линейный шаг по X
     xInc := iR / radius;
     yInc := xInc;

     //Функциональный шаг
     fxInc := xInc * cosX;
     fyInc := yInc * cosY;

     //дробные X и Y
     iX := 0;
     iY := 0;

     //Целочисленные X и Y
     x := 0;
     y := 0;

     //Пока (X > Y)
     repeat
       //Дробный Y
       iY := Sqrt(iR * iR - iX * iX);
       y := Round(Int(iY)); //Целая часть Y
       fY := (iY - y); //Дробная часть Y
       fAlpha := Round(fY * dAlpha); //Альфа дробного остатка пиксела
       pAlpha := dAlpha - fAlpha; //Альфа пиксела

         {difX := (maxX - iX);
         CorrectAlpha(difX, pAlpha, fAlpha);
         InfoMessage(Form3.Handle, 'x = y: ' +
                                   'макс f(X): ' + FloatToStrF(maxX, ffNumber, 18, 4) + '  ' +
                                   'iX: ' + FloatToStrF(iX, ffNumber, 18, 4) + '  ' +
                                   'difX: ' + FloatToStrF(difX, ffNumber, 18, 4));}

       if (iX > maxX) then
       begin
         if (x = y) then
         begin
         end
         else
         begin
           difX := (maxX - iX);
           CorrectAlpha(difX, pAlpha, fAlpha);
         end;
       end;

       //Пропустим нулевые пикселы
       //т.к. они уже стоят
       if (x > 0) then
       begin
         Set8XPixels(dX, dY, x, y, AColor, pAlpha, fAlpha, dOpacity);
       end;

       //Следующий дробный X
       iX := iX + xInc;
       //Округленный X
       x := Round(iX);
     until (x > y);

     //дробные X и Y
     iX := 0;
     iY := 0;

     //Целочисленные X и Y
     x := 0;
     y := 0;

     //Пока (Y > X)
     repeat
       //Дробный X
       iX := Sqrt(iR * iR - iY * iY);
       x := Round(Int(iX)); //Целая часть X
       fX := (iX - x); //Дробная часть X
       fAlpha := Round(fX * dAlpha); //Альфа дробного остатка пиксела
       pAlpha := dAlpha - fAlpha; //Альфа пиксела

       if (iY > maxY) then
       begin
         if (x = y) then
         begin
         end
         else
         begin
           difY := (maxY - iY);
           CorrectAlpha(difY, pAlpha, fAlpha);
         end;
       end;

       //Пропустим нулевые пикселы
       //т.к. они уже стоят
       if (y > 0) then
       begin
         Set8YPixels(dX, dY, x, y, AColor, pAlpha, fAlpha, dOpacity);
       end;

       iY := iY + yInc;
       y := Round(iY);
     until (y > x);
    end;

  • Sapersky (14.03.16 20:09) [20]
    Мне лень разбираться в этих ваших китайских алгоритмах, могу предложить рисование кольца с затухающей по краям альфой, при w = 2 результат аналогичен алгоритму Ву. Конечно, рисует медленнее (в 2-2.5 раза), поскольку это более универсальное решение.
    Закомментированный фрагмент без Sqrt работает немного быстрее, но кольцо получается не совсем симметричное (заметно на большой толщине).

    procedure DrawCircleW(Dst : TFastDIB; cx, cy, r, w : Single; c : DWord);
    Var y, sy, ey, BytesPP, Alpha : Integer;
       xf, xf2, r2, r2Min, r2Max, y2 : Single;
       ca : TFColorA;

     procedure DrawPixRange(sx, ex, y : Integer);
     Var x, a, ia : Integer;
         sy, d, w2, aw, aw2 : Single;
         pc : PFColor;
     begin
     Num_InRange(sx, 0, Dst.Width-1); Num_InRange(ex, 0, Dst.Width-1);
     pc := Dst.Scanlines[y]; Inc(PByte(pc), sx * BytesPP);
     sy := Sqr(y - cy);
     aw := Alpha / w;
     w2 := (r2Max - r2Min) * 0.5; aw2 := Alpha / w2;
     For x:=sx to ex do begin

       d := Abs(Sqrt(Sqr(x - cx) + sy) - r);
       If (d < w) then begin
         ia := Round(d * aw);

    {
       d := Abs( (Sqr(x - cx) + sy) - r2);
       If (d < w2) then begin
         ia := Round(d * aw2);
    }

         a := 255 - ia;
         pc.b := (pc.b * ia + ca.b * a) shr 8;
         pc.g := (pc.g * ia + ca.g * a) shr 8;
         pc.r := (pc.r * ia + ca.r * a) shr 8;
       end;
       Inc(PByte(pc), BytesPP);
     end;
     end;

    begin
    w := w * 0.5;
    BytesPP := Dst.Bpp shr 3;
    ca := TFColorA(c);
    Alpha := ca.a; If (Alpha = 0) then Alpha := $FF;

    sy := Trunc(cy - r - w); Num_InRange(sy, 0, Dst.AbsHeight-1);
    ey := Ceil(cy + r + w); Num_InRange(ey, 0, Dst.AbsHeight-1);

    r2Min := Sqr(r - w); r2Max := Sqr(r + w);
    r2 := r * r;

    For y := sy to ey do begin
     y2 := Sqr(y-cy);
     If y2 <= r2Max then begin
       xf := Sqrt( r2Max - y2 );
       If y2 > r2Min then DrawPixRange(Trunc(cx-xf), Ceil(cx+xf), y) else begin
         xf2 := Sqrt( r2Min - y2 );
         DrawPixRange(Trunc(cx-xf), Ceil(cx-xf2), y);
         DrawPixRange(Trunc(cx+xf2), Ceil(cx+xf), y);
       end;
     end;
    end;
    end;

  • Dmk © (14.03.16 20:25) [21]
    А что делает Num_InRange?
  • Dmk © (14.03.16 20:34) [22]
    И что за тип TFColorA и PFColor?
    У меня просто нет сторонних библиотек. Я свои пишу.
  • Sapersky (14.03.16 20:47) [23]
    Uses FastDIB, Utils; - из примера с многопоточностью, кот. я выкладывал.
  • Dmk © (15.03.16 09:33) [24]
    Да не. Кошмарный круг :(
    Ему до Wu далеко по сглаженности.
  • Dmk © (15.03.16 12:56) [25]
    На самом деле имеет смысл «попотеть» для хорошего результата.
    Кстати разница очевидна:
    http://hostingkartinok.com/show-image.php?id=19fc47c3b731f5f4352252f8b81654b0

    Справа Ваш круг.
  • Sapersky (15.03.16 13:16) [26]
    Это я наоптимизировал неудачно, не проверил при альфе < 255.
    Правильно так:

       If (d < w) then begin
         a := Alpha - Round(d * aw);
         ia := 255 - a;

  • Dmk © (17.03.16 11:54) [27]
    Может кто подскажет как узнать размер пикселя, если происходит разрыв функции?
    Например радиус = 101,5. Разрыв функции 2,2216. Функция sqrt(radius*radius - x*x). Для данной функции происходит периодичное накопление длины окружности.
  • Dmk © (18.03.16 16:27) [28]
    Ну и ладно. Сам справился. Нет никакого разрыва оказывается.

    Ровнехонькие получились :)
    http://hostingkartinok.com/show-image.php?id=975cbf34481d62542f34e8e77f38e086

    Чего и Вам желаю )
 
Конференция "Media" » Алгоритм Wu
Есть новые Нет новых   [118573   +14][b:0][p:0.012]