/*****************************************
*
* 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