Page d'accueil Description du projet
/*****************************************
 *
 *  Cedric Pradalier    2001
 *  mail : http://cedric.pradalier.free.fr/mail.html
 *
 * ***************************************/
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include "Pixmap.h"

void Pixmap::TestPixmap()
{
    Pixmap P(100,100);

    P.DrawLine(0,0,99,99);
    P.DrawLine(0,99,99,0);
    P.FloodFill(40,1);

    P.Store("TestFile.ppm");

}

Pixmap::Pixmap(int w,int h, unsigned char init)
{
    long size = (long)w* h;
    int i,j;
    datas = NULL;
    width = height = 0;
    alloc_pixmap(size);
    if (datas != NULL)
    {
        width = w;
        height = h;
        for (i=0;i<width;i++)
            for (j=0;j<height;j++)
                PutPixel(i,j,init);
    }
}
    
Pixmap::Pixmap(char * filename)
{
    datas = NULL;
    width = height = 0;
    Load(filename);
}
    

void Pixmap::Load(char*filename)
{
    Free();
    load_pixmap(filename);
    if (datas==NULL) width=height = 0;
}

Pixmap::~Pixmap()
{
    Free();
}

void Pixmap::Free()
{
    free(datas);
    datas = NULL;
    width = height = 0;
}

void Pixmap::Store(char * filename)
{
    store_pixmap(filename);
}

#ifdef unix
#include <unistd.h>
#include <sys/stat.h>
#define O_BINARY 0
#define IO_LEN  (1<<30) 
#endif

#if defined(THINK_C) || defined(__MWERKS__)
#include <unix.h>
#define IO_LEN  (1<<14) 
#endif

#ifndef FALSE
#define FALSE (0)
#define TRUE (1)
#endif

#ifdef sun
    extern char *sys_errlist[];
char *strerror(int err)
{
    return sys_errlist[err];
}
#endif


#define MAGIC_PGM   "P5\n"
#define MAGIC_PPM   "P6\n"

int Pixmap::match_key(int fd, char *key)
{
    char buf[80];

    read(fd, buf, strlen(key));
    if( strncmp(buf, key, strlen(key)) != 0 )
        return FALSE;
    else
        return TRUE;
}

void Pixmap::skip_comment(int fd, char code, char *c)
{
    while( *c == code )
    {
        while( (read(fd, c, 1) == 1 ) && (*c != '\n') ) ;
        read(fd, c, 1);
    }
}

void Pixmap::read_header_line(int fd, char *buf)
{
    int i;

    i = 1;
    while( (read(fd, &buf[i], 1) == 1 ) && (buf[i] != '\n') && (buf[i] != '\r') && (i<79) )
        i++;
    buf[i] = 0;
}

int Pixmap::get_pgm_header(int fd, char *magic)
{
    char buf[80];

    if( !match_key(fd, magic) )
        return FALSE;
    read(fd, buf, 1);
    skip_comment(fd, '#', buf);
    read_header_line(fd, buf);
    sscanf(buf, "%d %d", &width, &height);
    read(fd, buf, 1);
    skip_comment(fd, '#', buf);
    read_header_line(fd, buf);
    return TRUE;
}

int Pixmap::open_read(char *filename)
{
    int fd;

    if( (fd = open(filename, O_BINARY|O_RDONLY)) < 0 )
        fprintf(stderr, "can't reset file `%s': %s\n", filename, strerror(errno));
    return fd;
}

int Pixmap::open_read_pixmap(char *filename, char *magic)
{
    int fd;

    if( (fd = open_read(filename)) < 0)
        return fd;
    if( !get_pgm_header(fd, magic) )
    {
        fprintf(stderr, "can't read header of %s\n", filename);
        return -1;
    }
    return fd;
}

void Pixmap::alloc_pixmap(long size)
{
    Free();
    datas = new (unsigned char)[size];
    if( datas == NULL )
    {
        fprintf(stderr, "malloc error\n");
    }
}

void Pixmap::load_data(int fd,long size)
{
    char *buffer;
    int count;

    buffer = (char *)datas;
    while( size > 0 )
    {
        count = IO_LEN;
        if( count > size )
            count = size;
        read(fd, buffer, count);
        buffer += count;
        size -= count;
    }
}

void Pixmap::load_pixmap(char *filename)
{
    int fd;
    long size;

    if( (fd = open_read_pixmap(filename, MAGIC_PGM)) < 0)
        return ;
    size = (long)width * height;
    alloc_pixmap(size);
    if( datas != NULL )
        load_data(fd, size);
    close(fd);
}

void Pixmap::put_header_line(int fd, char *buf)
{
    write(fd, buf, strlen(buf));
}

void Pixmap::put_header_info(int fd, char *mark, char *filename)
{
    char buf[80];
    time_t now;

    sprintf(buf, "%sTitle: %s\n", mark, filename);
    put_header_line(fd, buf);
    now = time(NULL);
    sprintf(buf, "%sCreationDate: %s", mark, ctime(&now));
    put_header_line(fd, buf);
    sprintf(buf, "%sCreator: pixmap_io, P. Chassignet\n", mark);
    put_header_line(fd, buf);
}

void Pixmap::put_pgm_header(int fd, char *magic, char *filename)
{
    char buf[80];

    put_header_line(fd, magic);
    put_header_info(fd, "# ", filename);
    sprintf(buf, "%d %d\n255\n", width, height);
    put_header_line(fd, buf);
}

int Pixmap::open_write(char *filename)
{
    int fd;

#if defined(THINK_C) || defined(__MWERKS__)
    if( (fd = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_RDWR)) < 0 )
#else
    if( (fd = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_RDWR, S_IREAD|S_IWRITE)) < 0 )
#endif
            fprintf(stderr, "can't rewrite file `%s': %s\n", filename, strerror(errno));
    return fd;
}

void Pixmap::store_data(int fd, long size)
{
    char *buffer;
    int count;

    buffer = (char *)datas;
    while( size > 0 )
    {
        count = IO_LEN;
        if( count > size )
            count = size;
        write(fd, buffer, count);
        buffer += count;
        size -= count;
    }
}

void Pixmap::store_pixmap(char *filename)
{
    int fd;

    if( (fd = open_write(filename)) < 0 )
        return;
    put_pgm_header(fd, MAGIC_PGM, filename);
    store_data(fd, (long)width*height);
    close(fd);
}

#define INCR 1 
#define DECR -1 
#define PREDX 1 
#define PREDY 0 

void Pixmap::DrawLine ( int x1, int y1, int x2, int y2 , unsigned char color) 
{ 
    int t, i ; 
    int dx, dy, e, e_inc, e_noinc ;

    if ( x1 > x2 ) 
    { 
        t = x1 ; x1 = x2 ; x2 = t ; 
        t = y1 ; y1 = y2 ; y2 = t ; 
    }

    dx = x2 - x1 ; dy = y2 - y1 ; 

    if ( dx == 0 ) /* vertical line */ 
    { 
        if ( y1 > y2 ) 
        { 
            t = y1 ; y1 = y2 ; y2 = t ; 
        } 
        for ( i = y1 ; i <= y2 ; i++ ) 
            PutPixel ( x1, i, color ) ; 

        return ;
    } 

    if ( dy == 0 )  /* horizontal line */
    {
        for ( i = x1 ; i < x2 ; i++ ) 
            PutPixel ( i, y1, color ) ; 

        return ; 


    } 

    /* 0 < m < 1 */
    if ( dy < dx && dy > 0 )
    {
        e_noinc = 2 * dy ;
        e = 2 * dy - dx ;
        e_inc = 2 * ( dy - dx ) ;
        drawline ( x1, y1, x2, y2, PREDX, INCR , dx,dy,e,e_inc,e_noinc,color) ;
    } 

    /* m = 1 */
    if ( dy == dx && dy > 0 )
    {
        e_noinc = 2 * dy ;
        e = 2 * dy - dx ;
        e_inc = 2 * ( dy - dx ) ;
        drawline ( x1, y1, x2, y2, PREDX, INCR , dx,dy,e,e_inc,e_noinc,color) ;
    } 

    /* 1 < m < infinity */
    if ( dy > dx && dy > 0 )
    {
        e_noinc = 2 * dx ;
        e = 2 * dx - dy ;
        e_inc = 2 * ( dx - dy ) ;
        drawline ( x1, y1, x2, y2, PREDY, INCR , dx,dy,e,e_inc,e_noinc,color) ;
    } 

    /* 0 > m > -1 */
    if ( -dy < dx && dy < 0 )
    {
        dy = -dy ;
        e_noinc = 2 * dy ;
        e = 2 * dy - dx ;
        e_inc = 2 * ( dy - dx ) ;
        drawline ( x1, y1, x2, y2, PREDX, DECR , dx,dy,e,e_inc,e_noinc,color) ;
    } 

    /* m = -1 */
    if ( dy == -dx && dy < 0 )
    {
        dy = -dy ;
        e_noinc = ( 2 * dy ) ;
        e = 2 * dy - dx ;
        e_inc = 2 * ( dy - dx ) ;
        drawline ( x1, y1, x2, y2, PREDX, DECR , dx,dy,e,e_inc,e_noinc,color) ;
    } 

    /* -1 > m > 0 */
    if ( -dy > dx && dy < 0 )
    {
        dx = -dx ;
        e_noinc = - ( 2*dx ) ; e = 2 * dx - dy ;
        e_inc = - 2 * ( dx - dy ) ;
        drawline ( x2, y2, x1, y1, PREDY, DECR , dx,dy,e,e_inc,e_noinc,color) ;
    }
} 

void Pixmap::drawline ( int x1, int y1, int x2, int y2, int pred, int incdec ,
        int dx, int dy, int e, int e_inc, int e_noinc,
        unsigned char color
        ) 
{ 
    int i, start, end, var ;

    if ( pred == PREDX )
    {
        start = x1 ;  end = x2 ;  var = y1 ; 
    } 
    else
    {
        start = y1 ;  end = y2 ;  var = x1 ; 
    } 

    for ( i = start ; i <= end ; i++ )
    {
        if ( pred == PREDY ) 
            PutPixel ( var, i, color ) ; 
        else 
            PutPixel ( i, var, color ) ; 

        if ( e < 0 ) 
            e += e_noinc ; 
        else 
        { 
            var += incdec ; 
            e += e_inc ; 
        } 
    }
} 



struct seg {short y, xl, xr, dy;};  /* horizontal segment of scan line y */
void Pixmap::FloodFill(int x, int y, unsigned char color)
{
/*
 * fill.c : one page seed fill program, 1 channel frame buffer version
 *
 * doesn't read each pixel twice like the BASICFILL algorithm in
 *  Alvy Ray Smith, "Tint Fill", SIGGRAPH '79
 *
 * Paul Heckbert    13 Sept 1982, 28 Jan 1987
 */


#define MAX 10000       /* max depth of stack */

#define PUSH(Y, XL, XR, DY) \
    if (sp<stack+MAX && Y+(DY)>=wy1 && Y+(DY)<=wy2) \
    {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}

#define POP(Y, XL, XR, DY) \
    {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}



/*
 * fill: set the pixel at (x,y) and all of its 4-connected neighbors
 * with the same pixel value to the new pixel value color.
 * A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
 */
    int l, x1, x2, dy;
    int wx1 = 0, wx2 = width-1, wy1 = 0, wy2 = height-1;
    
    unsigned char ov;   /* old pixel value */
    struct seg stack[MAX], *sp = stack; /* stack of filled segments */

    ov = GetPixel(x, y);        /* read pv at seed point */
    if (ov==color || x<wx1 || x>wx2 || y<wy1 || y>wy2) return;
    PUSH(y, x, x, 1);           /* needed in some cases */
    PUSH(y+1, x, x, -1);        /* seed segment (popped 1st) */

    while (sp>stack) {
    /* pop segment off stack and fill a neighboring scan line */
    POP(y, x1, x2, dy);
    /*
     * segment of scan line y-dy for x1<=x<=x2 was previously filled,
     * now explore adjacent pixels in scan line y
     */
    for (x=x1; x>=wx1 && GetPixel(x, y)==ov; x--)
        PutPixel(x, y, color);
    if (x>=x1) goto skip;
    l = x+1;
    if (l<x1) PUSH(y, l, x1-1, -dy);        /* leak on left? */
    x = x1+1;
    do {
        for (; x<=wx2 && GetPixel(x, y)==ov; x++)
        PutPixel(x, y, color);
        PUSH(y, l, x-1, dy);
        if (x>x2+1) PUSH(y, x2+1, x-1, -dy);    /* leak on right? */
skip:       for (x++; x<=x2 && GetPixel(x, y)!=ov; x++);
        l = x;
    } while (x<=x2);
    }
}




#ifdef RGB_PIXMAPS
static void load_chunky(int fd, unsigned char *R_data, unsigned char *G_data, unsigned char *B_data, int width, int height)
{
    unsigned char *buffer, *buf, *buf_R, *buf_G, *buf_B;
    int count;

    buffer = alloc_pixmap(3L*width);
    buf_R = R_data;
    buf_G = G_data;
    buf_B = B_data;
    for( ; height > 0; height-- )
    {
        load_data(fd, buffer, 3L*width);
        count = width;
        buf = buffer;
        while( count-- > 0 )
        {
            *buf_R++ = *buf++;
            *buf_G++ = *buf++;
            *buf_B++ = *buf++;
        }
    }
    free(buffer);
}

static int load_RGB_pixmap(char *filename, int *width, int *height, unsigned char**R_data, unsigned char**G_data, unsigned char**B_data)
{
    int fd;
    long size;

    if( (fd = open_read_pixmap(filename, MAGIC_PPM, width, height)) < 0)
        return FALSE;
    size = (long)*width * *height;
    *R_data = alloc_pixmap(size);
    *G_data = alloc_pixmap(size);
    *B_data = alloc_pixmap(size);

    if( (*R_data != NULL) && (*G_data != NULL ) && (*B_data != NULL ))
    {
        load_chunky(fd, *R_data, *G_data, *B_data, *width, *height);
        close(fd);
        return TRUE;
    }

    close(fd);
    if( *R_data == NULL )
        return FALSE;
    free(*R_data);
    if( *G_data == NULL )
        return FALSE;
    free(*G_data);
    return FALSE;
}

static void store_chunky(int fd, unsigned char *R_data, unsigned char *G_data, unsigned char *B_data, int width, int height)
{
    unsigned char *buffer, *buf, *buf_R, *buf_G, *buf_B;
    int count;

    buffer = alloc_pixmap(3L*width);
    buf_R = R_data;
    buf_G = G_data;
    buf_B = B_data;
    for( ; height > 0; height-- )
    {
        count = width;
        buf = buffer;
        while( count-- > 0 )
        {
            *buf++ = *buf_R++;
            *buf++ = *buf_G++;
            *buf++ = *buf_B++;
        }
        store_data(fd, buffer, 3L*width);
    }
    free(buffer);
}


static void store_RGB_pixmap(char *filename, unsigned char *R_data, unsigned char *G_data, unsigned char *B_data, int width, int height)
{
    int fd;

    if( (fd = open_write(filename)) < 0 )
        return;
    put_pgm_header(fd, MAGIC_PPM, width, height, filename);
    store_chunky(fd, R_data, G_data, B_data, width, height);
    close(fd);
}
#endif