Las dos funciones más importantes gráficas son: el circulo y
la recta. A partir de ellas podremos
generar el resto de funciones.
En las entradas
anteriores he colocado la función básica de putpixel, para dibujar un punto,
con la cual se podrán generar el resto de las funciones. En este caso he usado,
por comodidad la Int 10h de la Bios., pero si alguien quiere puede hacer un putpixel
directamente sobre memoria de pantalla. Esta modificación no influirá en el
resto de las funciones ya que si se hace el putpixel de forma equivalente, el
resto de las funciones seguirán siendo válidas.
La recta es una
función en principio compleja si la queremos hacer sin usar operaciones
matemáticas complejas.
Actualmente,
debido a que los ordenadores son potentes, y se trabaja en alto nivel, a
cualquiera que se le proponga hacer una recta mediante pixel, utilizará la
ecuación de la recta y a correr. Pero si trabajamos en ensamblador, bien sea el
que estamos usando, o sea un microcontrolador, de manera que deseemos trazar
una línea en un papel mediante motores paso a paso, resulta problemático usar
coma flotante.
En 1965 Bresenham
creó unos algoritmos de aproximación para la recta y los círculos, los cuales
son capaces de trazar una recta usando solo operaciones incrementales y operaciones básicas de ensamblador como multiplica por 2 y sumar.
Este es el
algoritmo que he utilizado en esta función. Este algoritmo es válido solo si la
pendiente es mayor de cero y menor de infinito. Es decir no sirve para rectas
horizontales y verticales.
Por lo tanto estos
dos casos son tratados de forma independiente.
Además hay que
tratar el caso de trazar la recta en los dos sentidos, para lo cual lo que
realizamos es una comprobación del sentido para poner los incrementos (pasox y
pasoy) en +1 o -1 según convenga.
Además el algoritmo
solo sirve para un cuadrante, es decir no es lo mismo si la recta aumenta más
deprisa en x que en y que si la recta aumenta más deprisa en y que en x. Por
ello hay que verificar si Δx> Δy y tomar un algoritmo diferente.
Como el algoritmo
lo podréis encontrar en internet, pero nunca en ensamblador, he decidido hacer
un ejercicio de interpretación muy elegante.
Os he colocado
como comentario a la derecha el código equivalente en “C”. Es decir he puesto
el código en ensamblador, traduciéndolo a “C”. Esto servirá a muchos para
entender el algoritmo, pero a muchos más, para saber cómo codificar en
ensamblador cada directiva de “C”.
Esto que he hecho,
para mi es super sencillo, porque yo comencé con desensamblar y luego pase a “C”,
pero me imagino que para los programadores actuales, acostumbrados a lenguajes
de alto nivel, es muy difícil hacerse una idea de cómo traducir el “C” a
ensamblador.
En este caso, yo
he tomado el algoritmo en “C” y lo he traducido a ensamblador. Esto se puede
hacer con cualquier pseudocódigo, por lo que esta entrada es una forma de enseñar
este proceso.
El conjunto de la
librería estará formado por una macro, que deberá ser añadida a las cabeceras
GRAPHICS.MAC, que puse antes, y una
función, que deberá ser compilada y añadida a la librería mediante TLIB. Así
tendremos lista esta función para el futuro. Será usada como una instrucción más,
y no tendremos que volver a compilarla, ni a preocuparnos de ella.
Si se introduce,
como yo hago, dentro de una librería, y siempre usamos esa librería, la función
será transparente en nuestras aplicaciones.
Podéis ver cómo hacerlo en la pestaña COMPILAR del blog.
Con esta función
podremos realizar el rectangle y el fillrectangle, que pondré en la siguiente
entrada.
Más infornmación sobre el algoritmo Aquí
GRAPHICS.MAC
;Dibuja una línea
line macro x0,y0,x1,y1,color
ifndef _line
extrn _line:near
endif
mov ax,x0
push ax
mov ax,y0
push ax
mov ax,x1
push ax
mov ax,y1
push ax
mov ax,color
push ax
call _line
endm
LINEA.ASM
; Copyright (C) 2013 José Ángel Moneo Fernández ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program. If not, see <http://www.gnu.org/licenses/>.
include graphics.mac
.model compact,pascal
public _line
.data
incE dw 0
incNE dw 0
deltax dw 0 ;
deltay dw 0
pasox dw 0 ;paso en x
pasoy dw 0 ; paso en y
p dw 0 ;variable p procedimiento breshenham
x0 dw 0 ;parametros pasdados a linea
y0 dw 0 ;usado por todas las rutinas
x1 dw 0
y1 dw 0
color dw 0
.code
;algoritmo Bresenham
_line proc uses ax,px0,py0,px1,py1,pcolor
;paso de las variables de función a varialbes globales
;para uso en todas las rutinas
mov ax,px0
mov x0,ax
mov ax,py0
mov y0,ax
mov ax,px1
mov x1,ax
mov ax,py1
mov y1,ax
mov ax,pcolor
mov color,ax
;deltay=abs(yfinal-yinicial)
mov ax,y1
sub ax,y0 ; deltay=(y1-y0)
jns else_positivo1 ;if (deltay<0)
neg ax ;{abx(deltay)
mov pasoy,-1 ;pasoy=-1
jmp end_positivo1 ;}
else_positivo1: ;else
mov pasoy,1 ;pasoy=1
end_positivo1:
mov deltay,ax ; deltay=abs(y1-y0)
;deltax=abs(xfinal-xinicial)
mov ax,x1
sub ax,x0 ; deltax=(x1-x0)
jns elsepositivo2 ;if (deltax>0)
neg ax ;{abx(deltax)
mov pasox,-1 ;pasox=-1
jmp end_positivo2 ;}
elsepositivo2: ;else
mov pasox,1 ;pasox=1
end_positivo2:
mov deltax,ax ; deltax=abs(x1-x0)
cmp ax,0 ;if(deltax==0)
jne no_vertical ;{
call _linea_vertical ;linea_vertical
jmp fin_linea ;}
no_vertical:
mov ax,deltay
cmp ax,0 ;if(deltay==0)
jne no_horizontal ;{
call _linea_Horizontal ;linea_horizontal
jmp fin_linea ;}
no_horizontal:
;resto de lineas mediante algoritmo Bresenham
mov ax,deltax
cmp ax,deltay ;if(deltax>deltay)
jna alg2
call _algoritmo1 ; algoritmo1
jmp fin_linea
alg2: ;else
call _algoritmo2 ;algoritmo2
fin_linea:
ret
_line endp
_linea_vertical proc uses ax bx cx dx
mov bx,x0 ; x=x0
mov dx,y0 ; y=y0
mov cx,y1 ; yend=y1
while_vert:
cmp dx,cx ;while (y!=yend)
je fin_vert ;{
putpixel bx,dx,color ;pixel x,y
add dx,pasoy ;y+=pasoy
jmp while_vert
fin_vert:
ret
_linea_vertical endp
_linea_horizontal proc uses ax bx cx dx
mov bx,x0 ; x=x0
mov dx,y0 ; y=y0
mov cx,x1 ; xend=x1
while_hor:
cmp bx,cx ;while (x!=xend)
je fin_hor ;{
putpixel bx,dx,color ;pixel x,y
add bx,pasox ;x+=pasox
jmp while_hor
fin_hor:
ret
_linea_horizontal endp
;si deltax>deltay
_algoritmo1 proc uses ax bx cx dx
;p=2*deltay-deltax
mov ax,deltay
shl ax,1
mov incE,ax ;incE=2*deltay
sub ax,deltax
mov p,ax ;p=2*deltay-deltax
;incNE=2*(deltay-deltax)
mov ax, deltay
sub ax,deltax
shl ax,1
mov incNE,ax ;incNE=2*(deltay-deltax)
mov bx,x0 ; x=x0
mov dx,y0 ; y=y0
mov cx,x1 ; xend=x1
while1:
cmp bx,cx ;while (x!=xend)
je fin1 ;{
putpixel bx,dx,color ;pixel x,y
add bx,pasox ;x+=pasox
mov ax,p
cmp ax,0 ; if(p<0)
jns else_if1
add ax,incE
mov p,ax ;p+=incE
jmp short end_if1
else_if1: ;else
add ax,incNE
mov p,ax ;p+=incNE
add dx,pasoy ;y+=pasoy
end_if1:
jmp while1 ;}
fin1:
ret
_algoritmo1 endp
;si deltay>deltax
_algoritmo2 proc uses ax bx cx dx
;p=2*deltax-deltay
mov ax,deltax
shl ax,1
mov incE,ax ;incE=2*deltay
sub ax,deltay
mov p,ax ;p=2*deltax-deltay
;incNE=2*(deltax-deltay)
mov ax, deltax
sub ax,deltay
shl ax,1
mov incNE,ax ;incNE=2*(deltax-deltay)
mov bx,x0 ; x=x0
mov dx,y0 ; y=y0
mov cx,y1 ; yend=y1
while2:
cmp dx,cx ;while (y!=yend)
je fin2 ;{
putpixel bx,dx,color ;pixel x,y
add dx,pasoy ;y+=pasoy
mov ax,p
cmp ax,0 ; if(p<0)
jns else_if2
add ax,incE
mov p,ax ;p+=incE
jmp short end_if2
else_if2: ;else
add ax,incNE
mov p,ax ;p+=incNE
add bx,pasox ;x+=pasox
end_if2:
jmp while2 ;}
fin2:
ret
_algoritmo2 endp
end
No hay comentarios:
Publicar un comentario
Si tienes algún comentario, duda o sugerencia, o si quieres aportar algún código creado a partir de las librerías expuestas aquí, por favor, indícamelo.