This is libppm5.c in view mode; [Download] [Up]
/* libppm5.c - ppm utility library part 5 ** ** This library module contains the ppmdraw routines. ** ** Copyright (C) 1989, 1991 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #include "ppm.h" #include "ppmdraw.h" #define DDA_SCALE 8192 #if __STDC__ void ppmd_point_drawproc( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata ) #else /*__STDC__*/ void ppmd_point_drawproc( pixels, cols, rows, maxval, x, y, clientdata ) pixel** pixels; int cols, rows, x, y; pixval maxval; char* clientdata; #endif /*__STDC__*/ { if ( x >= 0 && x < cols && y >= 0 && y < rows ) pixels[y][x] = *( (pixel*) clientdata ); } /* Simple fill routine. */ #if __STDC__ void ppmd_filledrectangle( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, int width, int height, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_filledrectangle( pixels, cols, rows, maxval, x, y, width, height, drawprocP, clientdata ) pixel** pixels; int cols, rows, x, y, width, height; pixval maxval; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register int cx, cy, cwidth, cheight, col, row; /* Clip. */ cx = x; cy = y; cwidth = width; cheight = height; if ( cx < 0 ) { cx = 0; cwidth += x; } if ( cy < 0 ) { cy = 0; cheight += y; } if ( cx + cwidth > cols ) cwidth = cols - cx; if ( cy + cheight > rows ) cheight = rows - cy; /* Draw. */ for ( row = cy; row < cy + cheight; ++row ) for ( col = cx; col < cx + cwidth; ++col ) if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[row][col] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, col, row, clientdata ); } /* Outline drawing stuff. */ static int ppmd_linetype = PPMD_LINETYPE_NORMAL; int ppmd_setlinetype( type ) int type; { int old; old = ppmd_linetype; ppmd_linetype = type; return old; } static int ppmd_lineclip = 1; int ppmd_setlineclip( clip ) int clip; { int old; old = ppmd_lineclip; ppmd_lineclip = clip; return old; } #if __STDC__ void ppmd_line( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_line( pixels, cols, rows, maxval, x0, y0, x1, y1, drawprocP, clientdata ) pixel** pixels; int cols, rows, x0, y0, x1, y1; pixval maxval; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register int cx0, cy0, cx1, cy1; /* Special case zero-length lines. */ if ( x0 == x1 && y0 == y1 ) { if ( ( ! ppmd_lineclip ) || ( x0 >= 0 && x0 < cols && y0 >= 0 && y0 < rows ) ) if ( drawprocP == PPMD_NULLDRAWPROC ) ppmd_point_drawproc( pixels, cols, rows, maxval, x0, y0, clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, x0, y0, clientdata ); return; } /* Clip. */ cx0 = x0; cy0 = y0; cx1 = x1; cy1 = y1; if ( ppmd_lineclip ) { if ( cx0 < 0 ) { if ( cx1 < 0 ) return; cy0 = cy0 + ( cy1 - cy0 ) * ( -cx0 ) / ( cx1 - cx0 ); cx0 = 0; } else if ( cx0 >= cols ) { if ( cx1 >= cols ) return; cy0 = cy0 + ( cy1 - cy0 ) * ( cols - 1 - cx0 ) / ( cx1 - cx0 ); cx0 = cols - 1; } if ( cy0 < 0 ) { if ( cy1 < 0 ) return; cx0 = cx0 + ( cx1 - cx0 ) * ( -cy0 ) / ( cy1 - cy0 ); cy0 = 0; } else if ( cy0 >= rows ) { if ( cy1 >= rows ) return; cx0 = cx0 + ( cx1 - cx0 ) * ( rows - 1 - cy0 ) / ( cy1 - cy0 ); cy0 = rows - 1; } if ( cx1 < 0 ) { cy1 = cy1 + ( cy0 - cy1 ) * ( -cx1 ) / ( cx0 - cx1 ); cx1 = 0; } else if ( cx1 >= cols ) { cy1 = cy1 + ( cy0 - cy1 ) * ( cols - 1 - cx1 ) / ( cx0 - cx1 ); cx1 = cols - 1; } if ( cy1 < 0 ) { cx1 = cx1 + ( cx0 - cx1 ) * ( -cy1 ) / ( cy0 - cy1 ); cy1 = 0; } else if ( cy1 >= rows ) { cx1 = cx1 + ( cx0 - cx1 ) * ( rows - 1 - cy1 ) / ( cy0 - cy1 ); cy1 = rows - 1; } /* Check again for zero-length lines. */ if ( cx0 == cx1 && cy0 == cy1 ) { if ( drawprocP == PPMD_NULLDRAWPROC ) ppmd_point_drawproc( pixels, cols, rows, maxval, cx0, cy0, clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, cx0, cy0, clientdata ); return; } } /* Draw, using a simple DDA. */ if ( abs( cx1 - cx0 ) > abs( cy1 - cy0 ) ) { /* Loop over X domain. */ register long dy, srow; register int dx, col, row, prevrow; if ( cx1 > cx0 ) dx = 1; else dx = -1; dy = ( cy1 - cy0 ) * DDA_SCALE / abs( cx1 - cx0 ); prevrow = row = cy0; srow = row * DDA_SCALE + DDA_SCALE / 2; col = cx0; for ( ; ; ) { if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && row != prevrow ) { if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[prevrow][col] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, col, prevrow, clientdata ); prevrow = row; } if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[row][col] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, col, row, clientdata ); if ( col == cx1 ) break; srow += dy; row = srow / DDA_SCALE; col += dx; } } else { /* Loop over Y domain. */ register long dx, scol; register int dy, col, row, prevcol; if ( cy1 > cy0 ) dy = 1; else dy = -1; dx = ( cx1 - cx0 ) * DDA_SCALE / abs( cy1 - cy0 ); row = cy0; prevcol = col = cx0; scol = col * DDA_SCALE + DDA_SCALE / 2; for ( ; ; ) { if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && col != prevcol ) { if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[row][prevcol] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, prevcol, row, clientdata ); prevcol = col; } if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[row][col] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, col, row, clientdata ); if ( row == cy1 ) break; row += dy; scol += dx; col = scol / DDA_SCALE; } } } #define SPLINE_THRESH 3 #if __STDC__ void ppmd_spline3( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int x1, int y1, int x2, int y2, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_spline3( pixels, cols, rows, maxval, x0, y0, x1, y1, x2, y2, drawprocP, clientdata ) pixel** pixels; int cols, rows, x0, y0, x1, y1, x2, y2; pixval maxval; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register int xa, ya, xb, yb, xc, yc, xp, yp; xa = ( x0 + x1 ) / 2; ya = ( y0 + y1 ) / 2; xc = ( x1 + x2 ) / 2; yc = ( y1 + y2 ) / 2; xb = ( xa + xc ) / 2; yb = ( ya + yc ) / 2; xp = ( x0 + xb ) / 2; yp = ( y0 + yb ) / 2; if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH ) ppmd_spline3( pixels, cols, rows, maxval, x0, y0, xa, ya, xb, yb, drawprocP, clientdata ); else ppmd_line( pixels, cols, rows, maxval, x0, y0, xb, yb, drawprocP, clientdata ); xp = ( x2 + xb ) / 2; yp = ( y2 + yb ) / 2; if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH ) ppmd_spline3( pixels, cols, rows, maxval, xb, yb, xc, yc, x2, y2, drawprocP, clientdata ); else ppmd_line( pixels, cols, rows, maxval, xb, yb, x2, y2, drawprocP, clientdata ); } #if __STDC__ void ppmd_polyspline( pixel** pixels, int cols, int rows, pixval maxval, int x0, int y0, int nc, int* xc, int* yc, int x1, int y1, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_polyspline( pixels, cols, rows, maxval, x0, y0, nc, xc, yc, x1, y1, drawprocP, clientdata ) pixel** pixels; int cols, rows, x0, y0, nc, x1, y1; int* xc; int* yc; pixval maxval; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register int i, x, y, xn, yn; x = x0; y = y0; for ( i = 0; i < nc - 1; ++i ) { xn = ( xc[i] + xc[i + 1] ) / 2; yn = ( yc[i] + yc[i + 1] ) / 2; ppmd_spline3( pixels, cols, rows, maxval, x, y, xc[i], yc[i], xn, yn, drawprocP, clientdata ); x = xn; y = yn; } ppmd_spline3( pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1, drawprocP, clientdata ); } #if __STDC__ void ppmd_circle( pixel** pixels, int cols, int rows, pixval maxval, int cx, int cy, int radius, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_circle( pixels, cols, rows, maxval, cx, cy, radius, drawprocP, clientdata ) pixel** pixels; int cols, rows, cx, cy, radius; pixval maxval; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register int x0, y0, x, y, prevx, prevy, nopointsyet; register long sx, sy, e; x0 = x = radius; y0 = y = 0; sx = x * DDA_SCALE + DDA_SCALE / 2; sy = y * DDA_SCALE + DDA_SCALE / 2; e = DDA_SCALE / radius; if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[y + cy][x + cx] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, x + cx, y + cy, clientdata ); nopointsyet = 1; do { prevx = x; prevy = y; sx += e * sy / DDA_SCALE; sy -= e * sx / DDA_SCALE; x = sx / DDA_SCALE; y = sy / DDA_SCALE; if ( x != prevx || y != prevy ) { nopointsyet = 0; if ( drawprocP == PPMD_NULLDRAWPROC ) pixels[y + cy][x + cx] = *( (pixel*) clientdata ); else (*drawprocP)( pixels, cols, rows, maxval, x + cx, y + cy, clientdata ); } } while ( nopointsyet || x != x0 || y != y0 ); } /* Arbitrary fill stuff. */ typedef struct { short x; short y; short edge; } coord; typedef struct { int n; int size; int curedge; int segstart; int ydir; int startydir; coord* coords; } fillobj; #define SOME 1000 static int oldclip; char* ppmd_fill_init( ) { fillobj* fh; fh = (fillobj*) malloc( sizeof(fillobj) ); if ( fh == 0 ) pm_error( "out of memory allocating a fillhandle" ); fh->n = 0; fh->coords = (coord*) malloc( SOME * sizeof(coord) ); if ( fh->coords == 0 ) pm_error( "out of memory allocating a fillhandle" ); fh->size = SOME; fh->curedge = 0; /* Turn off line clipping. */ oldclip = ppmd_setlineclip( 0 ); return (char*) fh; } #if __STDC__ void ppmd_fill_drawproc( pixel** pixels, int cols, int rows, pixval maxval, int x, int y, char* clientdata ) #else /*__STDC__*/ void ppmd_fill_drawproc( pixels, cols, rows, maxval, x, y, clientdata ) pixel** pixels; int cols, rows, x, y; pixval maxval; char* clientdata; #endif /*__STDC__*/ { register fillobj* fh; register coord* cp; register coord* ocp; fh = (fillobj*) clientdata; if ( fh->n > 0 ) { /* If these are the same coords we saved last time, don't bother. */ ocp = &(fh->coords[fh->n - 1]); if ( x == ocp->x && y == ocp->y ) return; } /* Ok, these are new; check if there's room for two more coords. */ if ( fh->n + 1 >= fh->size ) { fh->size += SOME; fh->coords = (coord*) realloc( (char*) fh->coords, fh->size * sizeof(coord) ); if ( fh->coords == 0 ) pm_error( "out of memory enlarging a fillhandle" ); } /* Check for extremum and set the edge number. */ if ( fh->n == 0 ) { /* Start first segment. */ fh->segstart = fh->n; fh->ydir = 0; fh->startydir = 0; } else { register int dx, dy; dx = x - ocp->x; dy = y - ocp->y; if ( dx < -1 || dx > 1 || dy < -1 || dy > 1 ) { /* Segment break. Close off old one. */ if ( fh->startydir != 0 && fh->ydir != 0 ) if ( fh->startydir == fh->ydir ) { /* Oops, first edge and last edge are the same. ** Renumber the first edge in the old segment. */ register coord* fcp; int oldedge; fcp = &(fh->coords[fh->segstart]); oldedge = fcp->edge; for ( ; fcp->edge == oldedge; ++fcp ) fcp->edge = ocp->edge; } /* And start new segment. */ ++(fh->curedge); fh->segstart = fh->n; fh->ydir = 0; fh->startydir = 0; } else { /* Segment continues. */ if ( dy != 0 ) { if ( fh->ydir != 0 && fh->ydir != dy ) { /* Direction changed. Insert a fake coord, old ** position but new edge number. */ ++(fh->curedge); cp = &(fh->coords[fh->n]); cp->x = ocp->x; cp->y = ocp->y; cp->edge = fh->curedge; ++(fh->n); } fh->ydir = dy; if ( fh->startydir == 0 ) fh->startydir = dy; } } } /* Save this coord. */ cp = &(fh->coords[fh->n]); cp->x = x; cp->y = y; cp->edge = fh->curedge; ++(fh->n); } static int yx_compare ARGS((coord* c1, coord* c2)); static int yx_compare( c1, c2 ) coord* c1; coord* c2; { if ( c1->y > c2->y ) return 1; if ( c1->y < c2->y ) return -1; if ( c1->x > c2->x ) return 1; if ( c1->x < c2->x ) return -1; return 0; } #if __STDC__ void ppmd_fill( pixel** pixels, int cols, int rows, pixval maxval, char* fillhandle, void (*drawprocP)(pixel**, int, int, pixval, int, int, char*), char* clientdata ) #else /*__STDC__*/ void ppmd_fill( pixels, cols, rows, maxval, fillhandle, drawprocP, clientdata ) pixel** pixels; int cols, rows; pixval maxval; char* fillhandle; void (*drawprocP)(); char* clientdata; #endif /*__STDC__*/ { register fillobj* fh; int pedge, eq; register int i, leftside, edge, lx, rx, py; register coord* cp; fh = (fillobj*) fillhandle; /* Close off final segment. */ if ( fh->n > 0 && fh->startydir != 0 && fh->ydir != 0 ) if ( fh->startydir == fh->ydir ) { /* Oops, first edge and last edge are the same. */ register coord* fcp; int lastedge, oldedge; lastedge = fh->coords[fh->n - 1].edge; fcp = &(fh->coords[fh->segstart]); oldedge = fcp->edge; for ( ; fcp->edge == oldedge; ++fcp ) fcp->edge = lastedge; } /* Restore clipping now. */ (void) ppmd_setlineclip( oldclip ); /* Sort the coords by Y, secondarily by X. */ qsort( (char*) fh->coords, fh->n, sizeof(coord), yx_compare ); /* Find equal coords with different edge numbers, and swap if necessary. */ edge = -1; for ( i = 0; i < fh->n; ++i ) { cp = &(fh->coords[i]); if ( i > 1 && eq && cp->edge != edge && cp->edge == pedge ) { /* Swap .-1 and .-2. */ coord t; t = fh->coords[i-1]; fh->coords[i-1] = fh->coords[i-2]; fh->coords[i-2] = t; } if ( i > 0 ) { if ( cp->x == lx && cp->y == py ) { eq = 1; if ( cp->edge != edge && cp->edge == pedge ) { /* Swap . and .-1. */ coord t; t = *cp; *cp = fh->coords[i-1]; fh->coords[i-1] = t; } } else eq = 0; } lx = cp->x; py = cp->y; pedge = edge; edge = cp->edge; } /* Ok, now run through the coords filling spans. */ for ( i = 0; i < fh->n; ++i ) { cp = &(fh->coords[i]); if ( i == 0 ) { lx = rx = cp->x; py = cp->y; edge = cp->edge; leftside = 1; } else { if ( cp->y != py ) { /* Row changed. Emit old span and start a new one. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawprocP, clientdata); lx = rx = cp->x; py = cp->y; edge = cp->edge; leftside = 1; } else { if ( cp->edge == edge ) { /* Continuation of side. */ rx = cp->x; } else { /* Edge changed. Is it a span? */ if ( leftside ) { rx = cp->x; leftside = 0; } else { /* Got a span to fill. */ ppmd_filledrectangle( pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1, drawprocP, clientdata); lx = rx = cp->x; leftside = 1; } edge = cp->edge; } } } } /* All done. Free up the fillhandle and leave. */ free( fh->coords ); free( fh ); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.