Рисуем эллипс

Тема в разделе "WASM.DOS/BIOS/Vesa/ports", создана пользователем Mikl___, 30 ноя 2011.

  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Навеяно топиком
    Для начала строим эллипс инкрементируя [math]Х[/math] от [math]–b[/math] до 0, а для получения [math]Y[/math] используем уравнение эллипса [math]\left (\frac{X}{A}\right )^{2}+\left (\frac{Y}{B}\right )^{2}=1[/math] откуда [math]Y=B\cdot\sqrt{1–\left (\frac{X}{A}\right )^{2}}[/math], для скорости и используя симметрию выводим на экран через Set4Pixels сразу по четыре точки, все лишнее из цикла убрано, и так как умножение по-любому быстрее деления заменяем деление на [math]A^{2}[/math] умножением на [math]\frac{1}{A^{2}}[/math]. При построении смотрим не получился ли разрыв между точками и если [math]Y(X) – Y(X-1) > 1[/math], тогда строим линию с координатами [math](X-1, Y(X-1))[/math] и [math](X-1, Y(X))[/math]. На получившийся эллипс зеленого цвета, накладываем эллипс красного цвета построенный по алгоритму Брезенхейма. В результате наложения видно, что они на 1 пиксель не совпадают, то ли режим округления в FPU задан неверно, то ли хз? Полез в gdi32.dll откуда вызывается функция Ellips, оказывается из gdi32.Ellips через sysenter практически сразу уходим в ядро.
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny
    3. a equ 99
    4. b equ 63
    5. aa equ a*a
    6. bb equ b*b
    7. aa2 equ 2*aa
    8. bb2 equ 2*bb
    9. aa4 equ 4*aa
    10. bb4 equ 4*bb
    11. color1 equ 0Ah
    12. color2 equ 0Ch
    13. .code
    14. .386
    15. org 100h
    16. start:    mov ax,13h
    17.     int 10h
    18.     push 0A000h
    19.     pop es
    20.     finit
    21.     mov bp,x1
    22.     imul di,y1,320
    23.     call Set4Pixels1
    24.     mov ecx,b
    25.     fld1
    26.     fld st        ;st(0)=st(1)=1
    27.     fidiv b2        ;st(0)=-1/b²
    28.     fstp b2         ;b2=-1/b²
    29. @@:    inc x1
    30.     mov bx,y1
    31.     fild x1
    32.     fld st
    33.     fmulp st(1),st; st(0)=x*x
    34.     fmul b2       ; st(0)=-x²/b² st(1)=1 умножение быстрее деления
    35.     fadd st,st(1) ; st(0)=1-x²/b²
    36.     fsqrt         ; st(0)=√(1-x²/b²)
    37.     fimul a1      ; st(0)=a*√(1-x²/b²)
    38.     fistp y1
    39.     sub bx,y1    ;удаляем разрывы между точками
    40.     cmp bx,-b
    41.     jb l4
    42.     mov bp,x1
    43.     dec bp
    44. l5:    mov di,y1
    45.     add di,bx
    46.     imul di,320
    47.     call Set4Pixels1
    48.     inc bx
    49.     jnz l5
    50. l4:    mov bp,x1
    51.     imul di,y1,320
    52.     call Set4Pixels1
    53.     loop @b
    54.     mov ah,0
    55.     int 16h
    56. ;--------------Алгоритм Брезенхейма для рисования эллипса--------------------
    57.     mov eax,x    ;d=aa2*((x-1)*(x))+aa+bb2*(1-aa)
    58.     dec eax
    59.     imul x
    60.     imul eax,aa2
    61.     add eax,aa
    62.     add d,eax
    63.     mov eax,-aa
    64.     inc eax
    65.     imul eax,bb2
    66.     add d,eax
    67.     jmp l1        ;while(aa*x > bb*y)
    68. l0:    call Set4Pixels2
    69.     cmp d,0        ;if (d>=0)
    70.     jl @f
    71.     dec x        ;x--
    72.     imul esi,x,aa4
    73.     sub d,esi    ; d-=aa4*(x);
    74. @@:    mov esi,y
    75.     lea esi,[esi*2+3]
    76.     imul esi,bb2
    77.        add d,esi    ;d+=bb2*(3+y*2)
    78.        inc y        ;y++
    79. l1:    imul esi,x,aa
    80.     imul edi,y,bb
    81.     cmp esi,edi
    82.     jg l0
    83.     mov eax,y    ;d=bb2*(y+1)*y+aa2*(x*(x-2)+1)+(1-aa2)*bb
    84.     inc eax
    85.     imul y
    86.     imul eax,bb2
    87.     add d,eax
    88.     mov eax,-aa2
    89.     inc eax
    90.     imul eax,bb
    91.     add d,eax
    92.     mov eax,x
    93.     sub eax,2
    94.     imul eax,x
    95.     inc eax
    96.     imul eax,aa2
    97.     add d,eax
    98.     jmp l2
    99. l3:    call Set4Pixels2
    100.     cmp d,0        ;if (d<=0)
    101.     jg @f
    102.     inc y        ;y++
    103.     imul eax,y,bb4
    104.     add d,eax    ;d+=bb4*y
    105. @@:    dec x        ;x--
    106.     mov eax,x
    107.     lea eax,[eax*2-3];d+=aa2*(3-x*2)
    108.     imul eax,-aa2
    109.     add d,eax
    110. l2:    cmp x,-1
    111.     jnz l3
    112.     mov ah,0
    113.     int 16h
    114.     mov ax,3
    115.     int 10h
    116.     retn
    117. Set4Pixels1 proc
    118.     mov byte ptr es:[bp+32160][di],color1; X  Y
    119.     neg di
    120.     mov byte ptr es:[bp+32160][di],color1; X -Y
    121.     neg bp
    122.     mov byte ptr es:[bp+32160][di],color1;-X -Y
    123.     neg di
    124.     mov byte ptr es:[bp+32160][di],color1;-X  Y
    125.     retn
    126. Set4Pixels1 endp
    127. Set4Pixels2 proc
    128.     mov bp,word ptr x
    129.     imul di,word ptr y,320
    130.     mov byte ptr es:[bp+32160][di],color2; X  Y
    131.     neg di
    132.     mov byte ptr es:[bp+32160][di],color2; X -Y
    133.     neg bp
    134.     mov byte ptr es:[bp+32160][di],color2;-X -Y
    135.     neg di
    136.     mov byte ptr es:[bp+32160][di],color2;-X  Y
    137.     retn
    138. Set4Pixels2 endp
    139. x1 dw -b
    140. y1 dw 0
    141. x  dd b
    142. y  dd 0
    143. b2 dd -b*b
    144. a1 dd a
    145. d  dd ?
    146. end start
     
    Последнее редактирование: 23 апр 2023
  2. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Mikl___
    На GDI не обращай внимания, там с округлением вообще не заморачивались и эллипс иногда не вполне симметричный получается.
     
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Black_mirror
    Я убил на поиск подходящей реализации алгоритма Брезенхейма три дня, а оказалось либо эллипс по Брезенхейму рассчитывается неправильно, либо с округлением в FPU что-то не то...
     
  4. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Проблему решил выставлением режима "округление к нулю", аналогично работает режим "округление к −∞" заодно немного упростил реализацию алгоритма Брезенхейма
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny
    3. a equ 63
    4. b equ 99
    5. aa equ a*a
    6. bb equ b*b
    7. aa2 equ 2*aa
    8. bb2 equ 2*bb
    9. aa4 equ 4*aa
    10. bb4 equ 4*bb
    11. color1 equ 0Ah
    12. color2 equ 0Ch
    13. .code
    14. .386
    15. org 100h
    16. start: mov ax,13h
    17.     int 10h
    18.     push 0A000h
    19.     pop es
    20.     finit
    21.     push eax
    22.     fstcw [esp]
    23.     or word ptr [esp],0C00h; режим округления к нулю
    24.     fldcw [esp]
    25.     pop eax
    26.     mov bp,x1
    27.     xor di,di
    28.     call Set4Pixels1
    29.     mov ecx,b
    30.     fld1
    31.     fld st        ;st(0)=st(1)=1  
    32.     fidiv b2        ;st(0)=-1/b²
    33.     fstp b2         ;b2=-1/b²
    34. @@: inc x1
    35.     mov bx,y1       ;в bx старое значение Y
    36.     fild x1
    37.     fld st
    38.     fmulp st(1),st; st(0)=x*x
    39.     fmul b2       ; st(0)=-x²/b² st(1)=1 умножение быстрее деления
    40.     fadd st,st(1) ; st(0)=1-x²/b²
    41.     fsqrt         ; st(0)=√(1-x²/b²)
    42.     fimul a1      ; st(0)=a*√(1-x²/b²)
    43.     fistp y1
    44. ;удаляем разрывы между точками
    45.     sub bx,y1;смотрим разницу между старым значением Y и новым значением Y  
    46.     jns l3
    47.     neg bp      ;в bp старое значение X
    48. l2: add di,320  ;рисуем линию с координатами (Хстар, Yстар) и (Хстар, Yнов)
    49.     call Set4Pixels1
    50.     inc bx      ;пока разница не станет нулевой
    51.     js l2
    52. l3: mov bp,x1
    53.     call Set4Pixels1
    54.     loop @b
    55.     mov ah,0
    56.     int 16h
    57. ;--------------Алгоритм Брезенхейма для рисования эллипса--------------------
    58. l0: call Set4Pixels2
    59.     cmp d,0        ;if (d>=0)
    60.     jl @f
    61.     dec x        ;x--
    62.     imul esi,x,aa4
    63.     sub d,esi    ; d-=aa4*x;
    64. @@: mov esi,y
    65.     lea esi,[esi*2+3]
    66.     imul esi,bb2
    67.     add d,esi    ;d+=bb2*(3+y*2)
    68.     inc y        ;y++
    69.     imul esi,x,aa
    70.     imul edi,y,bb
    71.     cmp esi,edi     ;while(aa*x > bb*y)
    72.     jg l0;Остановимся, если Х перестала быть основной осью (дуга
    73. ;прошла точку наклона касательной в 45 градусов)
    74. ;-------------------------
    75.     mov eax,y    ;d=bb2*(y+1)*y+aa2*(x-1)²+(1-aa2)*bb
    76.     inc eax
    77.     imul y
    78.     imul eax,bb2
    79.     mov d,eax
    80.     mov eax,-aa2
    81.     inc eax
    82.     imul eax,bb
    83.     add d,eax
    84.     mov eax,x
    85.     dec eax
    86.     imul eax
    87.     imul eax,aa2
    88.     add d,eax
    89. ;--------------------------------
    90. l1: call Set4Pixels2
    91.     cmp d,0        ;if (d<=0)
    92.     jg @f
    93.     inc y        ;y++
    94.     imul eax,y,bb4
    95.     add d,eax    ;d+=bb4*y
    96. @@: mov eax,x
    97.     lea eax,[eax*2-5];d+=aa2*(5-x*2)
    98.     imul eax,-aa2
    99.     add d,eax
    100.     dec x        ;x--
    101.     jns l1;Остановимся, так как порошли через ось ординат
    102.     mov ah,0
    103.     int 16h
    104.     mov ax,3
    105.     int 10h
    106.     retn
    107. ;--процедуры вывода точки на экран
    108. Set4Pixels1 proc
    109.     mov byte ptr es:[bp+32160][di],color1; X  Y
    110.     neg di
    111.     mov byte ptr es:[bp+32160][di],color1; X -Y
    112.     neg bp
    113.     mov byte ptr es:[bp+32160][di],color1;-X -Y
    114.     neg di
    115.     mov byte ptr es:[bp+32160][di],color1;-X  Y
    116.     retn
    117. Set4Pixels1 endp
    118.  
    119. Set4Pixels2 proc
    120.     mov bp,word ptr x
    121.     imul di,word ptr y,320
    122.     mov byte ptr es:[bp+32160][di],color2; X  Y
    123.     neg di
    124.     mov byte ptr es:[bp+32160][di],color2; X -Y
    125.     neg bp
    126.     mov byte ptr es:[bp+32160][di],color2;-X -Y
    127.     neg di
    128.     mov byte ptr es:[bp+32160][di],color2;-X  Y
    129.     retn
    130. Set4Pixels2 endp
    131. ;--данные-----------------------
    132. x1 dw -b
    133. y1 dw 0
    134. x  dd b
    135. y  dd 0
    136. b2 dd -b*b
    137. a1 dd a
    138. d dd -aa2*b+aa+bb2
    139. end start
     
    Последнее редактирование: 23 дек 2016
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    немного упростил реализацию алгоритма Брезенхейма
    1) уравнение эллипса x²/b²+y²/a²=1 или x²a²+y²b²-a²b²=0 чтобы найти "точку перелома" берем производную d(x²a²+y²b²-a²b²)/dx=0 2a²x+2b²y*(dy/dx)=0 отсюда dy/dx=-2a²x/(2b²y)=-a²x/(b²y) ;
    в точке, где дуга прошла точку наклона касательной в 45 градусов dy/dx=-1 и существует равенство b²x=a²y
    2) параметры рисования для начальной точки (0,А)
    точка из предыдущей итерации (x,y) сравнивается с точкой (x+1,y-0.5) разница равна a²(x+1)²+b²(y-0.5)²-a²b²-a²x²-b²y²+a²b²=a²x²+2a²x+a²+b²y²-b²y+0.25b²-a²x²-b²y²=2a²x+a²-b²y+b²/4 ;
    в точке x=0 y=a d=2a²*0+a²-b²a+bb/4=a²-b²a+b²/4
    3) параметры рисования для начальной точки (B,0)
    точка из предыдущей итерации (x,y) сравнивается с точкой (x-0.5,y+1) разница равна a²(x-0.5)²+b²(y+1)²-a²b²-a²x²-b²y²+a²b²=a²x²-a²x+a²/4+b²y²+2b²y+b²-a²x²-b²y²=-a²x+a²/4+2b²y+b² ;
    в точке x=b y=0 d=-a²b+a²/4+b²
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny
    3. a equ 63
    4. b equ 99
    5. aa equ a*a
    6. bb equ b*b
    7. aa2 equ 2*aa
    8. bb2 equ 2*bb
    9. color equ 0Ch
    10. .code
    11. .386
    12. org 100h
    13. start:    mov ax,13h
    14.     int 10h
    15.     push 0A000h
    16.     pop es
    17.     mov esi,(bb/4)-bb*a+aa
    18.     xor ebp,ebp;x=0
    19.     mov edi,a*320;y=a
    20.     xor eax,eax;deltaX=0
    21. ;рисуем верхнюю и нижнюю точки
    22.     mov byte ptr es:[di+32160],color; 0  Y
    23. ;избавляемся от двух команд neg edi так как di равно a*320 то di-a*640=-a*320
    24.     mov byte ptr es:[di+32160-a*640],color; 0 -Y
    25. ;увеличиваем ошибку накопления до значения следующей точки по оси X
    26. l0:    add esi,eax    ;d+=deltaX
    27. ;рисуем четыре симметричные точки
    28.     mov byte ptr es:[bp+32161][di],color    ; X  Y
    29. ;избавляемся от команды inc ebp увеличив смещение по Х за счет смещения
    30. ;центра эллипса
    31.     neg edi                                
    32.     mov byte ptr es:[bp+32161][di],color    ; X -Y
    33.     not ebp; увеличиваем значение в ebp за счет того что not(X)=-(X+1)      
    34.     mov byte ptr es:[bp+32160][di],color    ;-X -Y
    35.     neg edi
    36.     mov byte ptr es:[bp+32160][di],color    ;-X  Y
    37.     neg ebp
    38. ;если ошибка накопления прошла 0, затем если координата Y прошла
    39. ;более чем полпути к следующему пикселю, то самое время перейти
    40. ;по оси Y на 1 пиксель и установить следующую ошибку накопления
    41.     test esi,esi    ;if (d>=0)
    42.     js @f
    43.     sub edi,320     ;y--
    44. ;уменьшаем deltaY в соответствие с координатой Y
    45.     sub deltaY,bb2  ;deltaY=2b²y
    46. ;уменьшаем ошибку накопления до значения следующей точки по оси Y
    47.     sub esi,deltaY    ;d-=2b²y
    48. ;увеличиваем deltaX в соответствие с координатой Х
    49. @@:    add eax,aa2    ;deltaX=2a²*x
    50.     cmp eax,deltaY  ;while(a²x > b²y)
    51.     jl l0;Остановимся, если Х перестала быть основной
    52. ;осью (дуга прошла точку наклона касательной в 45 градусов)
    53. ;-------------------------
    54.     mov ebp,b    ;x=b
    55. ;рисуем правую и левую точки
    56.     mov byte ptr es:[bp+32160],color    ; X  0
    57.     mov byte ptr es:[bp+32160-2*b],color    ;-X  0
    58.     xor edi,edi     ;y=0
    59.     xor eax,eax     ;deltaY=0
    60.     mov esi,(aa/4)-aa*b+bb
    61. ;--------------------------------
    62. ;увеличиваем ошибку накопления до значения следующей точки по оси Y
    63. l1:    add esi,eax    ;d+=deltaY
    64. ;увеличиваем deltaY в соответствие с координатой Y
    65.         add eax,bb2    ;deltaY=2b²*y
    66.     add edi,320     ;y++
    67. ;рисуем четыре симметричные точки
    68.     mov byte ptr es:[bp+32160][di],color; Х  Y
    69.     neg edi
    70.     mov byte ptr es:[bp+32160][di],color; X  -Y
    71.     neg ebp
    72.     mov byte ptr es:[bp+32160][di],color;-X -Y
    73.     neg edi
    74.     mov byte ptr es:[bp+32160][di],color;-X  Y
    75.     neg ebp      
    76. ;если ошибка накопления прошла 0, затем если координата Х прошла
    77. ;более чем полпути к следующему пикселю, то самое время перейти
    78. ;по оси Х на 1 пиксель и установить следующую ошибку накопления
    79.     test esi,esi    ;if (d<=0)
    80.     js @f
    81. ;уменьшаем deltaX в соответствие с координатой X
    82.     sub deltaX,aa2  ;deltaX=2a²*x
    83.     sub esi,deltaX  ;d-=deltaX
    84.     dec ebp         ;x--
    85. ;увеличиваем координату Y
    86. @@:    cmp eax,deltaX  ;while(b²y > a²x)
    87.     jl l1;Остановимся, если Y перестала быть основной
    88. ;осью (дуга прошла точку наклона касательной в 45 градусов)
    89. ;---------------------------------------
    90.     mov ah,0        ;ждем нажатие на любую клавишу
    91.     int 16h
    92.     mov ax,3    ;восстанавливаем текстовый режим
    93.     int 10h
    94.     retn        ;выходим из программы
    95. ;--------------------------------------------------
    96. deltaX dd aa2*b;2x*a²
    97. deltaY dd bb2*a;2y*b²
    98. end start
     
    Последнее редактирование: 23 дек 2016
  6. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Отделим мух от котлет
    алгоритм Брезенхейма на каждом шаге вычисляет положение точки, затем происходит вывод этой точки на экран и вычисление координат продолжается. Разделим создание эллипса на два этапа. Вычисление координат и рисование эллипса. Кроме того, если присмотреться, точки эллипса образуют набор вертикальных и горизонтальных линий разной длины смещенных относительно друг друга на один пиксель. Вычисляем длину каждой линии, которую потом поместим в массив ArrayX для горизонтальных и ArrayY для вертикальных линий, значения из массивов из-за симметрии будем читать или от начала к концу или от конца к началу. При этом явно задаются координаты только первой точки (в этом примере x=0 y=a)
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny
    3. screen_width equ 320
    4. screen_height equ 200
    5. a equ 63
    6. b equ 99
    7. aa equ a*a
    8. bb equ b*b
    9. aa2 equ 2*aa
    10. bb2 equ 2*bb
    11. color equ 0Ch
    12. .code
    13. .386
    14. org 100h
    15. start:    mov ax,13h
    16.     int 10h
    17.     push 0A000h
    18.     pop es
    19. ;--------------Алгоритм Брезенхейма для рисования эллипса--------------------
    20.     mov ebx,bb2*a    ;deltaY=2b²a
    21.     mov esi,(bb/4)-bb*a+aa
    22.     mov edi,offset ArrayX;адрес массива для длин отрезков
    23.     xor eax,eax    ;deltaX=0
    24. l0:    add esi,eax    ;d+=deltaX
    25.     inc byte ptr [edi];вычисляем длину отрезка
    26.     test esi,esi    ;if (d ≥ 0)
    27.     js @f
    28.     inc edi        ;переходим к следующей ячейке
    29.     inc countX    ;увеличмваем счетчик отрезков
    30.     sub ebx,bb2    ;deltaY=2yb²
    31.     sub esi,ebx    ;d-=2yb²
    32. @@:    add eax,aa2    ;deltaX=2xa²
    33.     cmp eax,ebx    ;while(a²x > b²y)
    34.     jl l0        ;Остановимся, если Х перестала быть основной
    35. ;осью (дуга прошла точку наклона касательной в 45 градусов)
    36.     mov ebx,aa2*b    ;deltaX=2xa²
    37.     mov edi,offset ArrayY;адрес массива для длин отрезков
    38.     xor eax,eax     ;deltaY=0
    39.     mov esi,(aa/4)-aa*b+bb
    40. l1:    add esi,eax    ;d+=deltaY
    41.         add eax,bb2    ;deltaY=2yb²
    42.     inc byte ptr [edi];вычисляем длину отрезка
    43.     test esi,esi    ;if (d ≤ 0)
    44.     js @f
    45.     inc edi         ;переходим к следующей ячейке
    46.     inc countY      ;увеличмваем счетчик отрезков
    47.     sub ebx,aa2    ;deltaX=2xa²
    48.     sub esi,ebx    ;d-=deltaX
    49. @@:    cmp eax,ebx    ;while(yb² > xa²)
    50.     jl l1
    51. ;заполняем первый квадрант---------------------
    52.     mov edi,a*screen_width+screen_width*(screen_height/2)+(screen_width/2);x=0 y=a
    53.     mov al,color    ;цвет эллипса
    54.     stosb        ;рисую точку (0,-a)
    55.     mov ebx,offset ArrayX ;двигаюсь вправо и вверх
    56.     call subrutineX ;читаю ArrayX от начала в конец
    57.     add edi,screen_width-1
    58.     dec delta;-(screen_width+1);двигаюсь вправо и вверх
    59.     mov esi,offset ArrayY-1
    60.     add esi,countY
    61.      call subrutineY        ;читаю ArrayY с конца и до начала
    62. ;заполняем второй квадрант------------------------
    63.     std
    64.     dec edi
    65.     stosb
    66.     sub edi,(screen_width-1)
    67.     neg delta1;delta1=-1   ;двигаюсь влево
    68.     mov delta,-(screen_width-1)  ;и вверх
    69.     inc esi
    70.     call subrutineY        ;читаю ArrayY от начала в конец
    71.     add edi,(screen_width+1)
    72.     dec delta;-screen_width  ;двигаюсь влево и вверх
    73.     dec ebx
    74.     call subrutineX ;читаю ArrayX с конца и до начала
    75. ;заполняем третий квадрант-------------------------
    76.     add edi,screen_width
    77.     stosb
    78.     neg delta;screen_width    ;двигаюсь влево и вниз
    79.     inc ebx
    80.     call subrutineX ;читаю ArrayX от начала в конец
    81.     sub edi,(screen_width-1)
    82.     inc delta;screen_width+1   ;двигаюсь влево и вниз
    83.     dec esi
    84.     call subrutineY ;читаю ArrayY с конца и до начала
    85. ;заполняем четвертый квадрант---------------------
    86.     cld
    87.     inc edi
    88.     stosb
    89.     add edi,(screen_width-1)
    90.     neg delta1;,1    ;двигаюсь влево
    91.     mov delta,(screen_width-1)   ;и вниз
    92.     inc esi
    93.     call subrutineY ;читаю ArrayY от начала в конец
    94.     sub edi,(screen_width+1)
    95.     inc delta;screen_width    ;двигаюсь вправо и вверх
    96.     dec ebx
    97.     call subrutineX ;читаю ArrayX с конца и до начала
    98. ;-----------------------------------------------------------
    99.     mov ah,0        ;ждем нажатие на любую клавишу
    100.     int 16h
    101.     mov ax,3    ;восстанавливаем текстовый режим
    102.     int 10h
    103.     retn        ;выходим из программы
    104. ;--------------------------------------------------
    105. subrutineX proc
    106.     neg delta2
    107.     mov ecx,countX
    108. @@:    push ecx
    109.     mov cl,[ebx]
    110.     rep stosb    ;двигаемся по горизонтали
    111.     add edi,delta    ;смещаемся по вертикали на 1 пиксель вверх или вниз
    112.     add ebx,delta2  ;двигаемся по массиву вперед или назад
    113.     pop ecx
    114.     loop @b
    115.     retn
    116. subrutineX endp
    117. subrutineY proc
    118.     neg delta2
    119.     mov ecx,countY
    120. l2:    push ecx
    121.     mov cl,[esi]
    122. @@:    stosb           ;смещаемся по горизонтали на 1 пиксель
    123.     add edi,delta    ;двигаемся по вертикали вверх или вниз
    124.     loop @b
    125.     add edi,delta1    ;edi = edi ± 1
    126.     add esi,delta2  ;двигаемся по массиву вперед или назад
    127.     pop ecx
    128.     loop l2
    129.     retn
    130. subrutineY endp
    131. ;------------------------------
    132. delta    dd -screen_width
    133. delta1    dd 1
    134. delta2    dd -1
    135. countX    dd 0
    136. countY    dd 0
    137. ArrayX db a/2 dup(0)
    138. ArrayY db a/2 dup(0)
    139. end start
     
    Последнее редактирование: 23 дек 2016
  7. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Выше эллипс выводился без использования битовой маски в 256-цветном 13h графическом режиме. Приведенная программа выводит эллипсы в следующих графических режимах
    номер режимаразрешение в пикселяхколичество цветов
    0Dh 320x200 16 цветов
    0Eh640x20016 цветов
    0Fh640x350 монохромный
    10h640x35016 цветов
    11h640x480монохромный
    12h640x48016 цветов
     
  8. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Переделано из Абраш М. "Таинства программирования графики" К.: ЕвроСИБ, 1996
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny
    3. .code
    4. .386
    5. org 100h
    6. SCREEN_WIDTH_IN_BYTES    equ 80;число байтов в строке в режиме 12h
    7. GC_INDEX        equ 3CEh
    8. SET_RESET_INDEX        equ 0  ;индекс регистра установки/сброса в GC
    9. SET_RESET_ENABLE_INDEX    equ 1; индекс регистра разрешения установки/сброса в GC
    10. BIT_MASK_INDEX        equ 8; регистр битовой маски
    11. ;рисуем эллипс заданных радиусов по осям X и Y и заданным цветом, используя
    12. ;метод с целыми числами и без извлечения корней, генерируя дугу для одного
    13. ;октанта в буфер, рисуем четыре симметричных дуги из этого буфера. Затем делаем
    14. ;то же самое для другой основной оси.
    15. start:    mov ax,12h
    16.     int 10h       ;640x480x16
    17.     push 0A000h
    18.     pop es        ;es=0A000h
    19. @@:    cmp YRadius,240
    20.     ja exit
    21.     push Color1
    22.     push YRadius
    23.     push XRadius
    24.     push 480/2
    25.     push 640/2
    26.     call DrawEllips
    27.     dec Color1
    28.     dec XRadius
    29.     add YRadius,2
    30.     jmp @b
    31. exit:    mov ah,0
    32.     int 16h        ;ждем пока не надавят на клаву
    33.     mov ax,3        ;восстанавливаем текстовый режим
    34.     int 10h
    35.     retn        ;выходим из программы
    36. ;----------------------------------------
    37. ;рисуем эллипс радиусом А по оси Х, радиусом В по оси Y и цветом Color, центр
    38. ;в точке (x, y). Радиусы должны быть положительными
    39. DrawEllips proc
    40. PixListPtr    equ word ptr  [bp-22h]
    41. BB2    equ dword ptr [bp-20h]
    42. AA2    equ dword ptr [bp-1Ch]
    43. BB    equ dword ptr [bp-18h]
    44. AA    equ dword ptr [bp-14h]
    45. YAdjust        equ dword ptr [bp-10h]
    46. XAdjust        equ dword ptr [bp-0Ch]
    47. Threshold    equ dword ptr [bp-8]
    48. WorkingY    equ word ptr [bp-4]
    49. WorkingX    equ word ptr [bp-2]
    50. X        equ word ptr [bp+4]
    51. Y        equ word ptr [bp+6]
    52. A        equ word ptr [bp+8]
    53. B        equ word ptr [bp+0Ah]
    54. Color        equ word ptr [bp+0Ch]
    55.         push bp
    56.     mov bp,sp
    57.     sub sp,22h
    58.     push si
    59.     push di
    60.     mov si,X
    61.     mov di,Y
    62.     movzx eax,B
    63.     mul eax
    64.     mov BB,eax    ; BB = B*B
    65.     shl eax,1
    66.     mov BB2,eax    ; BB2 =  B*B*2
    67. ;установим цвет с помощью установки/сброса
    68.     mov dx,GC_INDEX
    69.     mov ax,0F00h or SET_RESET_ENABLE_INDEX
    70.     out dx,ax        ;outpw(GC_INDEX, (0x0F00) | SET_RESET_ENABLE_INDEX)
    71. ;разрешим установку/сброс для всех плоскостей
    72.     mov ah,Color
    73.     mov al,0
    74.     out dx, ax        ;outpw(GC_INDEX, (Color << 8) | SET_RESET_INDEX)
    75. ;установим цвет установки/сброса. Оставим индексный регистр GC указывающим на
    76. ;регистр битовой маски
    77.     mov al,BIT_MASK_INDEX
    78.     out dx,al        ;outp(GC_INDEX, BIT_MASK_INDEX)
    79. ;рисуем четыре симмитричные дуги, для которых продвижение по оси Х идет быстрее,
    80. ;то есть для которых Х - основная ось. Установим параметры рисования для первой
    81. ;точки (0, В). Вычислим все точки вдоль дуги размером 1/8 эллипса и разместим
    82. ;эту информацию в PixList
    83.     mov PixListPtr,offset PixList;PixListPtr = PixList
    84.     mov WorkingX,0        ;WorkingX = 0
    85.     mov XAdjust,0        ;XAdjust = 0
    86.     movzx eax,A
    87.     mul eax
    88.     mov AA,eax    ; AA = A*A
    89.     shl eax,1
    90.     mov AA2,eax    ; AA = A*A*2
    91.     movzx edx,B
    92.     imul eax,edx
    93.     mov YAdjust,eax        ;YAdjust = AA * 2 * B
    94.     sar eax,1        ;eax = AA * B
    95.     mov ebx,AA
    96.     shr ebx,2        ;ebx = AA/4
    97.     sub ebx,eax      
    98.     mov Threshold,ebx    ;Threshold = AA/4 - AA * B
    99. ;инкрементируем ошибку накопления до значения для следующей точки по оси Х
    100. b1:    mov eax,XAdjust
    101.     add eax,BB
    102.     add Threshold,eax    ;Threshold += XAdjust + BB
    103. ;если ошибка накопления прошла 0, значит, координатаа Y прошла более чем полпути
    104. ;к следующему пикселю и самое время перейти по оси Y и остановить новую ошибку
    105. ;накопления
    106.     cmp Threshold,0       ;if (Threshold >= 0)
    107.     mov bx,PixListPtr
    108.     jl b2
    109.     mov eax,AA2
    110.     sub YAdjust,eax        ;YAdjust -= AA * 2
    111.     mov eax,YAdjust
    112.     sub Threshold,eax       ;Threshold -= YAdjust
    113. ;перейдем по обеим осям
    114.     mov byte ptr [bx],1     ;*PixListPtr++ = 1
    115.     jmp short b3
    116. ;перейдем только вдоль оси Х
    117. b2:    mov byte ptr [bx],0     ;else *PixListPtr++ = 0
    118. b3:    inc PixListPtr
    119. ;инкрементируем координату Х
    120.     mov eax,BB2
    121.     add XAdjust,eax         ;XAdjust += BB * 2
    122.     inc WorkingX            ;WorkingX++
    123. ;остановимся, если Х больше не основная ось (дуга прошла точку наклона
    124. ;касательной в 45)
    125.     mov eax,XAdjust
    126.     cmp eax,YAdjust         ;if(XAdjust >= YAdjust ) break
    127.     jl b1
    128. ;-----------------------------------------------------------------
    129. ;рисуем каждый из четырех симметричных октантов, для которых Х основная ось.
    130. ;Дуги через одну выравниваем, чтобы не произошло наложения
    131.         push offset PixList
    132.     push 0
    133.     push SCREEN_WIDTH_IN_BYTES
    134.     push WorkingX
    135.     mov ax,di;Y
    136.     sub ax,B
    137.     push ax;Y-B
    138.     push si;X
    139.     call DrawHOctant;(X, Y-B, WorkingX, SCREEN_WIDTH_IN_BYTES,0,PixList)
    140.     push offset PixList+1
    141.     push 1
    142.     push SCREEN_WIDTH_IN_BYTES
    143.     mov ax,WorkingX
    144.     dec ax
    145.     push ax;WorkingX-1
    146.     movzx ax,PixList
    147.     add ax,di;Y
    148.     sub ax,B
    149.     push ax;Y-B+(*PixList)
    150.     mov dx,si
    151.     inc dx
    152.     push dx;X+1
    153.     call DrawHOctant;(X+1, Y-B+(*PixList), WorkingX-1, SCREEN_WIDTH_IN_BYTES, 1, PixList+1)
    154.     push offset PixList
    155.     push 0
    156.     push -SCREEN_WIDTH_IN_BYTES
    157.     push WorkingX
    158.     mov ax,di
    159.     add ax,B
    160.     push ax
    161.     push si
    162.     call DrawHOctant;(X, Y+B, WorkingX, -SCREEN_WIDTH_IN_BYTES,0,PixList)
    163.     push offset PixList+1
    164.     push 1
    165.     push -SCREEN_WIDTH_IN_BYTES
    166.     mov ax,WorkingX
    167.     dec ax
    168.     push ax
    169.     movzx ax,PixList
    170.     mov dx,di
    171.     add dx,B
    172.     sub dx,ax
    173.     push dx
    174.     mov ax,si
    175.     inc ax
    176.     push ax
    177.     call DrawHOctant;(X+1, Y+B-(*PixList), WorkingX-1, -SCREEN_WIDTH_IN_BYTES, 1, PixList+1)
    178. ;-----------------------------------------------------------
    179. ;рисуем четыре симметричные дуги, для которых продвижение по основной оси Х идет
    180. ;быстрее, то есть для которых Y - основная ось. Установим параметры рисования
    181. ;для начальной точки (А, 0). Вычислим все точки вдоль дуги 1/8 эллипса и
    182. ;разместим эту информацию в PixList
    183.     mov PixListPtr,offset PixList;  PixListPtr = PixList
    184.     mov WorkingY,0;  WorkingY = 0
    185.     mov YAdjust,0;  YAdjust = 0
    186.         mov ebx,BB
    187.     mov eax,ebx
    188.         movsx edx,A
    189.         imul ebx,edx        ;ebx = BB * A
    190.         shr eax,2        ;eax = BB / 4
    191.     sub eax,ebx        ;eax = BB / 4 - BB * A
    192.         mov Threshold,eax    ;Threshold = BB / 4 - BB * A
    193.     shl ebx,1        ;ebx = BB * 2 * A
    194.     mov XAdjust,ebx        ;XAdjust = BB * 2 * A
    195. b4:     mov eax,YAdjust
    196.     add eax,AA
    197.     add Threshold,eax    ;Threshold += YAdjust + AA
    198.     cmp Threshold,0
    199.         mov bx,PixListPtr
    200.     jl b5
    201.     mov eax,BB2
    202.     sub XAdjust,eax        ;XAdjust -= BB * 2
    203.         mov eax,XAdjust
    204.     sub Threshold,eax    ;Threshold -= XAdjust
    205.     mov byte ptr [bx],1    ;*PixListPtr++ = 1
    206.     jmp b6
    207. b5:    mov byte ptr [bx],0    ;*PixListPtr++ = 0
    208. b6:     inc PixListPtr
    209.     mov eax,AA2
    210.     add YAdjust,eax        ;YAdjust += AA * 2;
    211.     inc WorkingY        ;WorkingY++
    212.     mov eax,YAdjust
    213.     cmp eax,XAdjust      ;if(YAdjust >= XAdjust ) break;
    214.       jl b4
    215. ;------------------------------------------------
    216.         push offset PixList
    217.     push 1
    218.     push -SCREEN_WIDTH_IN_BYTES
    219.     push WorkingY
    220.     push di
    221.     mov ax,si
    222.     sub ax,A
    223.     push ax
    224.     call DrawVOctant;  DrawVOctant(X-A, Y, WorkingY, -SCREEN_WIDTH_IN_BYTES,1,PixList);
    225.     push offset PixList+1
    226.     push 1
    227.     push SCREEN_WIDTH_IN_BYTES
    228.     mov ax,WorkingY
    229.     dec ax
    230.     push ax
    231.     mov ax,di
    232.     inc ax
    233.     push ax
    234.     movzx ax, PixList
    235.     add ax,si
    236.     sub ax,A
    237.     push ax
    238.     call DrawVOctant;  DrawVOctant(X-A+(*PixList), Y+1, WorkingY-1, SCREEN_WIDTH_IN_BYTES,1,PixList+1);
    239.     push offset PixList
    240.     push 0
    241.     push -SCREEN_WIDTH_IN_BYTES
    242.     push WorkingY
    243.     push di
    244.     mov ax,si
    245.     add ax,A
    246.     push ax
    247.     call DrawVOctant;  DrawVOctant(X+A, Y, WorkingY, -SCREEN_WIDTH_IN_BYTES,0,PixList);
    248.     push offset PixList+1
    249.     push 0
    250.     push SCREEN_WIDTH_IN_BYTES
    251.     dec WorkingY
    252.     push WorkingY
    253.     inc di
    254.     push di
    255.     movzx ax,PixList
    256.     add si,A
    257.     sub si,ax
    258.     push si
    259.     call DrawVOctant;  DrawVOctant(X+A-(*PixList), Y+1, WorkingY-1, SCREEN_WIDTH_IN_BYTES,0,PixList+1);
    260.     mov dx,GC_INDEX+1
    261.     mov al,0FFh
    262.     out dx,al        ;  outp(GC_INDEX+1,0xFF)
    263.     dec dx
    264.     mov ax,SET_RESET_ENABLE_INDEX
    265.     out dx,ax        ;  outpw(GC_INDEX, SET_RESET_ENABLE_INDEX)
    266.     pop di
    267.     pop si
    268.     leave
    269.     retn 10
    270. DrawEllips endp
     
  9. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Код (ASM):
    1. ; Рисуем дугу в октанте, в котором Х - основная ось. (x,y) - это первый пиксел
    2. ;дуги. HorizontalMoveDirection выбирает направление движения вдоль дуги по
    3. ;горизонтали - влево или вправо (0=влево, 1=вправо). RowOffset содержит смещение
    4. ;в байтах от данной линии сканирования до следующей и проверяет, отрисовывается
    5. ;дуга вверх или вниз, DrawLength - это горизонтальная длина в пикселах дуги, а
    6. ;DrawList - это список, содержащий 0 для каждой точки, если следующая точка
    7. ;вертикально выровнена, и 1, если следующая точка на 1 пиксел по диагонали
    8. ;смещена влево или вправо
    9. DrawHOctant proc
    10. X            equ word ptr [esp+6]
    11. Y            equ word ptr [esp+8]
    12. DrawLength        equ word ptr [esp+10]
    13. RowOffset        equ word ptr [esp+12]
    14. HorizontalMoveDirection    equ word ptr [esp+14]
    15. DrawList        equ word ptr [esp+16]
    16.  
    17.     push si
    18.     push di
    19.     imul dx,Y,SCREEN_WIDTH_IN_BYTES
    20.     mov di,X
    21.     mov cx,di
    22.     sar di,3
    23.     add di,dx        ;di = ScreenPtr = (Y * SCREEN_WIDTH_IN_BYTES)+(X/8))
    24.     and cx,7
    25.     mov ax,80h
    26.     sar ax,cl
    27.     mov cl,al        ;cl = BitMask = 0x80 >> (X & 7)
    28.     mov si,DrawLength    ;while (DrawLenght--)
    29. b1:    mov dx,GC_INDEX+1
    30.     mov al,cl        ;al = BitMask
    31.     out dx,al        ;outp(GC_INDEX + 1, BitMask)
    32.     or byte ptr es:[di],0FEh;*ScreenPtr |= 0xFE
    33.     mov bx,DrawList
    34.     inc DrawList
    35.     cmp byte ptr [bx],0    ;if(*DrawList++)
    36.     jz b2
    37.     add di,RowOffset    ;ScreenPtr += RowOffset
    38. b2:     cmp HorizontalMoveDirection,1
    39.     jnz b3
    40.     shr cl,1        ;BitMask = BitMask >> 1
    41.     jnz b4                  ;BitMask = 0?
    42.     mov cl,80h        ;BitMask = 80h
    43.     inc di            ;ScreenPtr++
    44.     jmp b4
    45. b3:    shl cl,1        ;BitMask = BitMask << 1
    46.     jnz b4                  ;BitMask = 0?
    47.     mov cl,1        ;BitMask = 1
    48.     dec di            ;ScreenPtr--
    49. b4:    dec si
    50.     jnz b1
    51.     pop di
    52.     pop si
    53.     retn 12
    54. DrawHOctant endp
    55. ; Рисуем дугу в октанте, в котором Y - основная ось. (x,y) - это первый пиксел
    56. ;дуги. HorizontalMoveDirection выбирает направление движения вдоль дуги по
    57. ;горизонтали - влево или вправо (0=влево, 1=вправо). RowOffset содержит смещение
    58. ;в байтах от данной линии сканирования до следующей и проверяет, отрисовывается
    59. ;дуга вверх или вниз, DrawLength - это вертикальная длина в пикселах дуги, а
    60. ;DrawList - это список, содержащий 0 для каждой точки, если следующая точка
    61. ;вертикально выровнена, и 1, если следующая точка на 1 пиксел по диагонали
    62. ;смещена влево или вправо
    63. DrawVOctant proc
    64. X            equ word ptr [esp+6]
    65. Y            equ word ptr [esp+8]
    66. DrawLength        equ word ptr [esp+10]
    67. RowOffset        equ word ptr [esp+12]
    68. HorizontalMoveDirection    equ word ptr [esp+14]
    69. DrawList        equ word ptr [esp+16]
    70.  
    71.     push si
    72.     push di
    73. ;позиционируемся на байт, в котором находится первый пиксель
    74.     imul dx,Y,SCREEN_WIDTH_IN_BYTES
    75.     mov di,X
    76.     mov cx,di        ;cx = X
    77.     sar di,3
    78.     add di,dx        ;di = Y * SCREEN_WIDTH_IN_BYTES + (X/8))
    79. ;установим начальную битовую маску
    80.     and cx,7
    81.     mov ax,80h
    82.     shr ax,cl
    83.     mov cl,al        ;cl = BitMask = 0x80 >> (X & 7)
    84. ;рисуем все точки в DrawList
    85.     mov si,DrawLength    ;while (DrawLenght--)
    86.     test si,si
    87.     jz b4
    88. b1:    mov dx,GC_INDEX+1    ;установим битовую маску пиксела
    89.     mov al,cl        ;al = BitMask
    90.     out dx,al        ;outp(GC_INDEX + 1, BitMask)
    91. ;рисуем пиксел. OR выполним, чтобы принудительно считать/записать для загрузки
    92. ;карманов. Записываемые данные значения не имеют, потому что установка/сброс
    93. ;разрешена для всех плоскостей
    94.     or byte ptr es:[di],0FEh;*ScreenPtr |= 0xFE
    95. ;перейдем на следующий пиксель в соответствии с DrawList
    96.     mov bx,DrawList
    97.     inc DrawList
    98.     cmp byte ptr [bx],0    ;if(*DrawList++)
    99.     jz b3
    100. ;шагнем по горизонтали, чтобы создать диагональное перемещение. Сдвинем битовую
    101. ;маску, перемещаясь на один байт по горизонтали, если битовая маска вырождается
    102.     cmp HorizontalMoveDirection,1
    103.     jnz b2
    104.     shr cl,1        ;BitMask = BitMask >> 1
    105.     jnz b3                  ;BitMask = 0?
    106.     mov cl,80h        ;BitMask = 80h
    107.     inc di            ;ScreenPtr++
    108.     jmp b3
    109. b2:    shl cl,1        ;BitMask = BitMask << 1
    110.     jnz b3                  ;BitMask = 0?
    111.     mov cl,1        ;BitMask = 1
    112.     dec di            ;ScreenPtr--
    113. b3:    add di,RowOffset        ;ScreenPtr += RowOffset
    114.     dec si
    115.     jnz b1
    116. b4:    pop di
    117.     pop si
    118.     retn 12
    119. DrawVOctant endp
    120. Color1 dw 7
    121. XRadius dw 319
    122. YRadius dw 1
    123. ;максимальная длина вдоль основной оси равна 1/2 ширины экрана
    124. PixList db SCREEN_WIDTH_IN_BYTES*8/2 dup(0)
    125. end start
    126.  
    рисование эллипса с помощью 3 режима записи VGA, что позволяет для рисования в графических режимах 0Dh, 0Eh, 10h, 11h, 12h избежать использования тормозов в виде команд OUT при установке пиксела
    Код (ASM):
    1. ; masm dos com #
    2. .model tiny,stdcall
    3. SCREEN_WIDTH_IN_BYTES    equ 80    ;число байтов в строке в режиме 12h
    4. GC_INDEX        equ 3CEh;порт графического контроллера (GC)
    5. SET_RESET_INDEX        equ 0      ;индекс регистра установки/сброса в GC
    6. SET_RESET_ENABLE_INDEX    equ 1    ;индекс регистра разрешения установки/сброса в GC
    7. BIT_MASK_INDEX        equ 8    ;регистр битовой маски
    8. GC_MODE_INDEX         equ 5    ;индекс регистра графического режима в GC
    9. COLOR_DONT_CARE     equ 7   ;индекс регистра безразличия цвета в GC
    10. BIT_MASK_INDEX         equ 8     ;регистр битовой маски
    11. .code
    12. .386
    13. org 100h
    14. start:    push 0A000h
    15.     pop es            ;es := 0A000h
    16.     mov ax,12h              ;установим графический режим 640x480x16
    17.     int 10h
    18. ;---------------------------------------------
    19. @@:    push Color1
    20.     push YRadius
    21.     push XRadius
    22.     push 480/2        ;y-координата центра
    23.     push 640/2        ;x-координата центра
    24.     call DrawEllips         ;в цикле выводим 120 эллипсов с разными радиусами
    25.     dec Color1              ;и разными цветами
    26.     dec XRadius
    27.     add YRadius,2
    28.     cmp YRadius,240
    29.     jb @b
    30.     mov ah,0        ;ждем нажатия на клаву
    31.     int 16h
    32.     mov ax,3                ;восстанавливаем текстовый режим
    33.     int 10h
    34.     ret                     ;выходим из программы
     
    >Quiet Snow< нравится это.
  10. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Код (ASM):
    1. ;рисуем эллипс заданных радиусов и заданным цветом по осям Х и Y, используя
    2. ;быстрый метод с целыми числами без извлечения корней, генерируя координаты
    3. ;дуги октанта в буфер PixelList и затем рисуем восемь дуг, используя
    4. ;вычисленные координаты
    5. ;--------------------------------------------------
    6. DrawEllips proc X,Y,A,B,Color:word
    7. local OriginalGCMode:byte,Threshold:dword,AA:dword,BB:dword
    8. local XAdjust:dword,YAdjust:dword,DrawLength:word
    9.  
    10.     mov di,X
    11. ;установим цвет с помощью установки/сброса
    12.     mov dx,GC_INDEX
    13.     mov ax,0F00h or SET_RESET_ENABLE_INDEX
    14. ;AH=значение для регистра разрешения установки/сброса (все битовые плоскости      
    15.     out dx,ax    ;разрешены) AL=номер регистра установки/сброса      
    16. ;разрешим установку/сброс для всех плоскостей
    17.     mov ah,byte ptr Color    ;AH=значение пиксела
    18.     mov al,SET_RESET_INDEX    ;AL=номер регистра установки/сброса в GC
    19.     out dx,ax    ;установим цвет установки/сброса
    20.     mov al,GC_MODE_INDEX
    21.     out dx,al  
    22.     inc dx
    23.     in al,dx
    24.     mov OriginalGCMode,al
    25.     or al,0Bh  
    26.     out dx,al
    27.     dec dx
    28.     mov ax,COLOR_DONT_CARE
    29.     out dx,ax
    30.     mov ax,0FF00h or BIT_MASK_INDEX
    31.     out dx,ax
    32. ;готовимся к расчету координат дуги, где основная координата по оси X  
    33.     movzx eax,B
    34.     mul eax
    35.     mov BB,eax    ;BB = B * B
    36.     push eax    ;MinorSquared = BB
    37.     movzx eax,A
    38.     mul eax
    39.     mov AA,eax    ;AA = A * A
    40.     push eax    ;MajorSquared = AA
    41.     movzx edx,B
    42.     mul edx        ;eax = B*AA
    43.     mov edx,AA
    44.     shr edx,2
    45.     sub edx,eax
    46.     push edx    ;Threshold = AA/4 - B*AA
    47.     add eax,eax
    48.     push eax    ;MinorAdjust = 2*B*AA
    49.     call GenerateEOctant
    50. ;рисуем четыре симметричные дуги вдоль оси Х
    51. ;рисуем 1-ую дугу
    52.     push SCREEN_WIDTH_IN_BYTES
    53.     mov ax,Y
    54.     sub ax,B        ;Y-B
    55.     call DrawHLOctant
    56. ;рисуем 2-ую дугу
    57.     push SCREEN_WIDTH_IN_BYTES
    58.     mov ax,Y
    59.     sub ax,B        ;Y-B
    60.     call DrawHROctant
    61. ;рисуем 3-ью дугу
    62.     push -SCREEN_WIDTH_IN_BYTES
    63.     mov ax,Y
    64.     add ax,B        ;Y+B
    65.     call DrawHLOctant
    66. ;рисуем 4-ую дугу
    67.     push -SCREEN_WIDTH_IN_BYTES
    68.     mov ax,Y
    69.     add ax,B        ;Y+B
    70.     call DrawHROctant
    71. ;готовимся к расчету координат дуги, где основная координата по оси Y  
    72.     push AA         ;MinorSquared = AA
    73.     mov eax,BB
    74.     push eax    ;MajorSquared = BB
    75.     movzx edx,A
    76.     mul edx        ;eax = A*BB
    77.     mov edx,BB
    78.     shr edx,2
    79.     sub edx,eax
    80.     push edx    ;Threshold = BB/4 - A*BB
    81.     shl eax,1
    82.     push eax    ;MinorAdjust = 2*A*BB
    83.     call GenerateEOctant
    84. ;рисуем четыре симметричные дуги вдоль оси Y
    85. ;рисуем 5-ую дугу
    86.     push -SCREEN_WIDTH_IN_BYTES
    87.     push Y
    88.     mov ax,di
    89.     sub ax,A    ;X-A
    90.     call DrawVROctant
    91. ;рисуем 6-ую дугу
    92.     push SCREEN_WIDTH_IN_BYTES
    93.     push Y        ;Y
    94.     mov ax,di
    95.     sub ax,A    ;X-A
    96.     call DrawVROctant
    97. ;рисуем 7-ую дугу
    98.     push -SCREEN_WIDTH_IN_BYTES
    99.     push Y
    100.     mov ax,di
    101.     add ax,A    ;X+A
    102.     call DrawVLOctant
    103. ;рисуем 8-ую дугу
    104.     push SCREEN_WIDTH_IN_BYTES
    105.     push Y        ;Y
    106.     mov ax,di
    107.     add ax,A    ;X+A
    108.     call DrawVLOctant
    109. ;восстанавливаем первоначальный режим записи
    110.     mov ah,OriginalGCMode
    111.     mov al,GC_MODE_INDEX
    112.     mov dx,GC_INDEX
    113.     out dx,ax
    114. ;восстанавливаем нормальное состояние регистра безразличия цвета
    115.     mov ax,0F00h or COLOR_DONT_CARE
    116.     out dx,ax
    117. ;включим разрешение установки/сброса
    118.     mov ax,SET_RESET_ENABLE_INDEX
    119.     out dx,ax
    120.     ret
    121. DrawEllips endp
    122. ;------------------------------------------------
    123. ;генрируем октант заданного радиуса, помещая резудьтаты в PixelList, где 0
    124. ;означает, что пикселы рисуются только вдоль основной оси, а 1, что рисуем
    125. ;пиксел по диагонали. Функция возвращает количество точек в дуге.
    126. ;----------------------------------------------
    127. GenerateEOctant proc
    128. ;параметры
    129. MinorSquared        equ dword ptr [esp+22]
    130. MajorSquared        equ dword ptr [esp+18]
    131. Threshold        equ dword ptr [esp+14]
    132. MinorAdjust        equ dword ptr [esp+10]
    133. ;адрес возврата из процедуры           [esp+6]
    134. ;локальные переменные
    135. MajorSquaredDouble    equ dword ptr [esp+4]
    136. MinorSquaredDouble    equ dword ptr [esp]
    137.  
    138.     add sp,-8        ;место под локальные переменные
    139.     xor si,si        ;установим счетчик в ноль
    140.     mov eax,MajorSquared
    141.     shl eax,1
    142.     mov MajorSquaredDouble,eax
    143.     mov eax,MinorSquared
    144.     shl eax,1
    145.     mov MinorSquaredDouble,eax
    146. ;установим MajorAdjust=0
    147.     xor ecx,ecx        ;ecx - MajorAdjust
    148.     mov ebx,Threshold    ;ebx - ошибка накопления
    149. GenLoop: add ebx,ecx;инкрементируем ошибку накопления на MajorAdjust+MinorAxis^2
    150.     add ebx,MinorSquared
    151. ;если ошибка накопления прошла ноль, то координата по неосновной оси прошла
    152. ;более чем полпути к следующему пикселю и самое время сделать шаг вдоль
    153. ;неосновной оси и переустановить ошибку накопления
    154.     mov PixelList[si],0;положим, что не будем двигаться вдоль неосновной оси
    155.     js MoveMajor
    156. ;координата вдоль неосновной оси изменилась.
    157. ;Подправим координату по неосновной оси.
    158.     mov eax,MajorSquaredDouble
    159.     sub MinorAdjust,eax
    160. ;подправим ошибку накопления при шаге вдоль неосновной оси
    161.     sub ebx,MinorAdjust
    162.     mov PixelList[si],1
    163. MoveMajor: inc si        ;посчитаем эту точку
    164. ;подправим координату вдоль основной оси для новой точки
    165.     add ecx,MinorSquaredDouble
    166. ;остановимся, если основная ось поменялась (дуга прошла точку наклона
    167. ;касательной в 45 градусов)
    168.     cmp ecx,MinorAdjust
    169.     jb GenLoop
    170. Done:    ;вернем число пикселей через SI
    171.     add sp,8        ;удаляем локальные переменные
    172.     retn 10h
    173.  
    174. GenerateEOctant endp
    175. ;--------------------------------------------------------------------------
    176. ;Рисуем дугу в октанте, в котором Y - основная ось. (x,y) - это первый пиксел
    177. ;дуги. Функция выбирает направление движения вдоль дуги по горизонтали - влево
    178. ;или вправо (L=влево, R=вправо). RowOffset содержит смещение в байтах от данной
    179. ;линии сканирования до следующей и проверяет, отрисовывается дуга вверх или
    180. ;вниз, в AX - X, в SI - вертикальная длина в пикселах дуги, PixelList - список,
    181. ;содержащий 0 для каждой точки, если следующая точка вертикально выровнена,
    182. ;и 1, если следующая точка на 1 пиксел по диагонали смещена влево или вправо