Search code examples
cdosvga

Drawing a line: Problems with the mouse cursor on C Vga graphics


I´m trying to make a program where you can draw a line by clicking the mouse, the line will then change size and direction while you move the with the button pressed and will stay on the screen when you release it (Just as in paint or any other photo editor). I´m working on C with 256-VGA. The problem I´m having is then drawing a line on a space where the mouse cursor is. I have this two functions to show and hide the mouse:

typedef struct MOUSE{
    byte on;
    byte button1;
    byte button2;
    byte button3;
    int num_buttons;
    sword x;
    sword y;
    byte under[MOUSE_SIZE];
    MOUSEBITMAP *bmp;


} MOUSE;
/**************************************************************************
 *  show_mouse                                                            *
 *    Displays the mouse.                                                 *
 **************************************************************************/
void show_mouse(MOUSE *mouse){
    int x,y;
    int mx = mouse->x - mouse->bmp->hot_x;
    int my= mouse->y - mouse->bmp->hot_y;
    long screen_offset = (my<<8)+(my<<6);
    word bitmap_offset = 0;
    byte data;
    //memcpy(double_buffer,VGA,SCREEN_SIZE);
    /* allocate memory for double buffer and background image */
    /*if ((double_buffer = (byte *) malloc(SCREEN_SIZE)) == NULL)
    {
        printf("Not enough memory for double buffer.\n");
        exit(1);
    }   */
    for(y=0;y<MOUSE_HEIGHT;y++){
        for(x=0;x<MOUSE_WIDTH;x++,bitmap_offset++){
            mouse->under[bitmap_offset] = VGA[(word) (screen_offset+mx+x)];
            if(mx+x < SCREEN_WIDTH && mx+x >=0 &&
                my+y < SCREEN_HEIGHT && my+y >=0) //Verificando que estemos dentro de pantalla
                {
                    /*Pintando en pantalla*/
                    data = mouse->bmp->data[bitmap_offset];
                    if(data) 
                    {
                        VGA[(word)(screen_offset+mx+x)] = data;
                    }                       
                }           
        }
        screen_offset+=SCREEN_WIDTH;
    }
    /*show_buffer(double_buffer);
    free(double_buffer);*/
}
 /**************************************************************************
 *  hide_mouse                                                            *
 *    hides the mouse.  This code is not optimized.                       *
 **************************************************************************/

void hide_mouse(MOUSE *mouse){

    int x,y;
    int mx = mouse->x - mouse->bmp->hot_x;
    int my = mouse->y - mouse->bmp->hot_y;
    long screen_offset = (my<<8)+(my<<6);
    word bitmap_offset = 0;
    /*memcpy(double_buffer,VGA,SCREEN_SIZE);
    /* allocate memory for double buffer and background image */
    /*if ((double_buffer = (byte *) malloc(SCREEN_SIZE)) == NULL)
    {
        printf("Not enough memory for double buffer.\n");
        exit(1);
    }   */
    for(y=0;y<MOUSE_HEIGHT;x++,y++){

        for(x=0;x<MOUSE_WIDTH;x++,bitmap_offset++){
            //Verificando que este dentro de pantalla
            if(mx+x < SCREEN_WIDTH &&mx+x >=0 &&
                my+y <SCREEN_HEIGHT && my+y >=0){

                    /*Pintando en pantalla*/

                    VGA[(word)(screen_offset+mx+x)] = mouse->under[bitmap_offset];
                }

        }
        screen_offset+=SCREEN_WIDTH;

    }
    /*show_buffer(double_buffer);
    free(double_buffer);*/
} 

I have the under variable of my MOUSE struct, which has the background of the screen before it´s drawn. In order to make the line move with the mouse when button is held down I store the previous position of the mouse and draw a white line (to clean it up) and then another line with the new position.

The problem is that then I make a line smaller in size and move the mouse pointer along the line, the repainting of the mouse is drawing once again the line that was before. I´m getting something like this:

Before shrinking the line: enter image description here

After Shrinking the line:

enter image description here

This is the rest of the code related to the loop controlling the mouse buttonsvoid realizar_accion(){

void realizar_accion(){
    /*Caso1: Pintar pixel simple (lapiz)*/
    if(accion==1){

        plot_pixel(mouse_g.x,mouse_g.y,2);
        plot_pixel(mouse_g.x-1,mouse_g.y,2);
        plot_pixel(mouse_g.x,mouse_g.y-1,2);
        plot_pixel(mouse_g.x-1,mouse_g.y-1,2);

    }
    /*Caso2: Pintar Linea*/
    else if(accion==2){
        init_x = mouse_g.x;
        prevx = mouse_g.x;
        init_y = mouse_g.y;
        prevy = mouse_g.y;      
        //line(mouse_g.x,mouse_g.y,mouse_g.x-20,mouse_g.y-20,2);
    }
}
void realizar_accion_mantenido(){
    /*Caso1: Pintar pixel simple (lapiz)*/
    if(accion==1){

        plot_pixel(mouse_g.x,mouse_g.y,2);
        plot_pixel(mouse_g.x-1,mouse_g.y,2);
        plot_pixel(mouse_g.x,mouse_g.y-1,2);
        plot_pixel(mouse_g.x-1,mouse_g.y-1,2);


    }
    /*Caso2: Pintar Linea*/
    else if(accion==2){
        if(on_draw==-1){

            line(init_x,init_y,mouse_g.x,mouse_g.y,2);
            prevx=mouse_g.x;
            prevy=mouse_g.y;
            on_draw=0;

        }
        else{
            if(prevx!=mouse_g.x&&prevy!=mouse_g.y){
                line(init_x,init_y,prevx,prevy,0xFFFF); //borrando la linea anterior
                line(init_x,init_y,mouse_g.x,mouse_g.y,2);
                prevx=mouse_g.x;
                prevy=mouse_g.y;
            }

        }



    }


}

int sobre_barra(){
    if(new_x1>0 && new_x1<33 &&new_y1>0 &&new_y1<181){


        return 1;

    }
    else{

        return -1;
    }


}
void boton_soltado(){

    /*Caso1: Pintar pixel simple (lapiz)*/
    if(accion==1){



    }
    /*Caso2: Pintar Linea*/
    else if(accion==2){
        if(on_draw==0){
            line(init_x,init_y,prevx,prevy,0xFFFF); //borrando la linea anterior
             wait_for_retrace();
             hide_mouse(&mouse_g);
             if (mouse_new!=NULL) mouse_g.bmp=mouse_new;
             mouse_g.x = new_x1;
             mouse_g.y=new_y1;
             show_mouse(&mouse_g);
            line(init_x,init_y,mouse_g.x,mouse_g.y,2);
            prevx=mouse_g.x;
            prevy=mouse_g.y;
            on_draw=-1;         

        }

    }



}
void boton_mantenido(){

    /*Verificar que este dento del buffer de dibujo....*/
    if(sobre_barra()!=1){
        realizar_accion_mantenido();

    }
}
void boton_presionado(){

    if(sobre_barra()==1){
        cambiar_herramienta();

    }
    else{

        realizar_accion();

    }   

}
/**************************************************************************
 *  paint_screen                                                      *
 *    show  main screen paint                                           *
 **************************************************************************/
void paint_screen(BITMAP *fondo){
    int mantenido;
    BITMAP barra,barra_color,normal_ptr_image;
    int anterior_presionado;
    word last_time;
    word redraw,press,release;
    sword dx,dy=0;
    MOUSEBITMAP *mouse_new=NULL;    
    int i,done = 0;
    on_draw=-1;
    accion =2;
    current_color=2;
    /*Pintando fondo blanco*/
    clear_screen();
    /*Pintando barra de herramientas*/
    load_bmp("normal.bmp",&normal_ptr_image);
    load_bmp("mainbar.bmp",&barra);
    load_bmp("colores.bmp",&barra_color);
    set_pallete(fondo->pallete);
    draw_bitmap(&barra,0,0);
    draw_bitmap(&barra_color,0,180);
    load_mouse(&mouse_g);
    show_mouse(&mouse_g);
    wait(50);
    while(!done){
         if(redraw){
             wait_for_retrace();
             hide_mouse(&mouse_g);
             if (mouse_new!=NULL) mouse_g.bmp=mouse_new;
             mouse_g.x = new_x1;
             mouse_g.y=new_y1;
             show_mouse(&mouse_g);
             redraw=0;
             mouse_new=NULL;
         }

        do {                              // check mouse status 
            anterior_presionado = press;
            get_mouse_motion(&dx,&dy);
            press   = get_mouse_press(LEFT_BUTTON);
            release = get_mouse_release(LEFT_BUTTON);
            //Si el estado estaba presionado y no se ha soltado.. el boton esta mantenido
            if(anterior_presionado==1 &&release==0){
              mantenido =1;

            }
        } while (dx==0 && dy==0 && press==0 && release==0&&*my_clock==last_time);    
        if (release){
            mouse_g.button1=0;
            mantenido=0;
            boton_soltado();


        }       

        if (press){

            mouse_g.button1=1;
            boton_presionado();


        } 
        //El boton se mantiene presionado
        else if(mantenido){
            boton_mantenido();
        }
        else{
            release=1;
        }


        if (dx || dy)                     // calculate movement 
        {
          new_x1 = mouse_g.x+dx;
          new_y1 = mouse_g.y+dy; //Actualizamos posicion mouse
          if (new_x1<0)   new_x1=0;
          if (new_y1<0)   new_y1=0;
          if (new_x1>319) new_x1=319;
          if (new_y1>199) new_y1=199;
          redraw=1;
        }

        if(new_x1>=287 && new_x1 <320
            && new_y1>=180 && new_y1 < 200 &&press){

        set_mode(TEXT_MODE); 
        printf("Adios!!");
        wait(25);
        done=0;
        return;


    }   
    }
}

Can anyone help me solve this issue?


Solution

  • I don't recommend drawing a white line over the old line. Instead, you should be using a frame-buffer, which is an off-screen memory area where you re-assemble it each time. Draw the background, then the line. Once you place the line, draw it onto the background.. but until then it's drawn afterwards. Then the whole thing is flipped/copied to the screen on each loop. Make sense?