line drawing

This commit is contained in:
John Cupitt 2010-09-27 17:14:22 +00:00
parent 9330273f51
commit aad2eb8703
13 changed files with 736 additions and 663 deletions

View File

@ -24,6 +24,8 @@
nip2 can call inplace ops directly now
- added im_draw_rect(), moved im_paintrect() to deprecated
- added im_draw_image(), moved im_insertplace() to deprecated
- added im_draw_line(), now clips, moved im_fastline() to deprecated
- added im_draw_line_user(), now clips, moved im_fastlineuser() to deprecated
12/5/10 started 7.22.2
- the conditional image of ifthenelse can be any format, a (!=0) is added if

View File

@ -524,3 +524,12 @@ im_fastline( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel )
{
return( im_draw_line( im, x1, y1, x2, y2, pel ) );
}
int
im_fastlineuser( IMAGE *im,
int x1, int y1, int x2, int y2,
int (*fn)(), void *client1, void *client2, void *client3 )
{
return( im_draw_line_user( im, x1, y1, x2, y2,
fn, client1, client2, client3 ) );
}

View File

@ -240,6 +240,9 @@ int im_flood_other_copy( IMAGE *test, IMAGE *mark, IMAGE *out,
int x, int y, int serial );
int im_fastline( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel );
int im_fastlineuser( IMAGE *im,
int x1, int y1, int x2, int y2,
int (*fn)(), void *client1, void *client2, void *client3 );
#ifdef __cplusplus
}

View File

@ -37,30 +37,32 @@
extern "C" {
#endif /*__cplusplus*/
int im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r );
int im_smear( IMAGE *im, int ix, int iy, Rect *r );
int im_smudge( IMAGE *im, int ix, int iy, Rect *r );
int im_draw_rect( IMAGE *image,
int left, int top, int width, int height, int fill, PEL *ink );
int im_draw_circle( IMAGE *im,
int cx, int cy, int radius, gboolean fill, PEL *ink );
int im_draw_image( IMAGE *main, int x, int y, IMAGE *sub );
int im_draw_line( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel );
int im_fastlineuser( IMAGE *im,
typedef int (*VipsPlotFn)( VipsImage *im, int x, int y,
void *a, void *b, void *c );
int im_draw_line_user( VipsImage *im,
int x1, int y1, int x2, int y2,
int (*fn)(), void *client1, void *client2, void *client3 );
int im_readpoint( IMAGE *im, int x, int y, PEL *pel );
VipsPlotFn plot, void *a, void *b, void *c );
int im_draw_line( VipsImage *im, int x1, int y1, int x2, int y2, PEL *ink );
int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
int n, int *x1v, int *y1v, int *x2v, int *y2v );
int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
int im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
int im_flood_other( IMAGE *test, IMAGE *mark,
int x, int y, int serial, Rect *dout );
int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
int n, int *x1v, int *y1v, int *x2v, int *y2v );
int im_readpoint( IMAGE *im, int x, int y, PEL *pel );
int im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r );
int im_smear( IMAGE *im, int ix, int iy, Rect *r );
int im_smudge( IMAGE *im, int ix, int iy, Rect *r );
#ifdef __cplusplus
}

View File

@ -1,13 +1,15 @@
noinst_LTLIBRARIES = libinplace.la
libinplace_la_SOURCES = \
draw.c \
draw.h \
im_draw_circle.c \
flood.c \
im_draw_line.c \
im_draw_image.c \
im_draw_rect.c \
flood.c \
im_plotmask.c \
inplace_dispatch.c \
line_draw.c \
plot_point.c \
smudge_area.c

103
libvips/inplace/draw.c Normal file
View File

@ -0,0 +1,103 @@
/* base class for drawing operations
*
* 27/9/10
* - from im_draw_circle()
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <string.h>
#include <vips/vips.h>
#include "draw.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
/* Fill a scanline between points x1 and x2 inclusive. x1 < x2.
*/
void
im__draw_scanline( Draw *draw, int y, int x1, int x2 )
{
PEL *mp;
int i;
int len;
g_assert( x1 <= x2 );
if( y < 0 || y > draw->im->Ysize )
return;
if( x1 < 0 && x2 < 0 )
return;
if( x1 > draw->im->Xsize && x2 > draw->im->Xsize )
return;
x1 = IM_CLIP( 0, x1, draw->im->Xsize - 1 );
x2 = IM_CLIP( 0, x2, draw->im->Xsize - 1 );
mp = (PEL *) IM_IMAGE_ADDR( draw->im, x1, y );
len = x2 - x1 + 1;
for( i = 0; i < len; i++ ) {
im__draw_pel( draw, mp );
mp += draw->psize;
}
}
void
im__draw_free( Draw *draw )
{
IM_FREE( draw->ink );
}
Draw *
im__draw_init( Draw *draw, IMAGE *im, PEL *ink )
{
if( im_rwcheck( im ) )
return( NULL );
draw->im = im;
draw->ink = NULL;
draw->lsize = IM_IMAGE_SIZEOF_LINE( im );
draw->psize = IM_IMAGE_SIZEOF_PEL( im );
draw->noclip = FALSE;
if( ink ) {
if( !(draw->ink = (PEL *) im_malloc( NULL, draw->psize )) )
return( NULL );
memcpy( draw->ink, ink, draw->psize );
}
return( draw );
}

91
libvips/inplace/draw.h Normal file
View File

@ -0,0 +1,91 @@
/* base class for drawing operations
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/* Our state.
*/
typedef struct _Draw {
/* Parameters.
*/
IMAGE *im; /* Draw here */
PEL *ink; /* Copy of ink param */
/* Derived stuff.
*/
size_t lsize;
size_t psize;
/* If the object to draw is entirely within the image, we have a
* faster noclip path.
*/
gboolean noclip;
} Draw;
#define DRAW(X) ((Draw *)(X))
static inline void
im__draw_pel( Draw *draw, PEL *q )
{
int j;
/* Faster than memcopy() for n < about 20.
*/
for( j = 0; j < draw->psize; j++ )
q[j] = draw->ink[j];
}
/* Paint, with clip.
*/
static inline void
im__draw_pel_clip( Draw *draw, int x, int y )
{
if( x < 0 || x >= draw->im->Xsize )
return;
if( y < 0 || y >= draw->im->Ysize )
return;
im__draw_pel( draw, (PEL *) IM_IMAGE_ADDR( draw->im, x, y ) );
}
/* Is p painted?
*/
static inline gboolean
im__draw_painted( Draw *draw, PEL *p )
{
int j;
for( j = 0; j < draw->psize; j++ )
if( p[j] != draw->ink[j] )
break;
return( j == draw->psize );
}
void im__draw_scanline( Draw *draw, int y, int x1, int x2 );
void im__draw_free( Draw *draw );
Draw *im__draw_init( Draw *draw, IMAGE *im, PEL *ink );

View File

@ -26,6 +26,8 @@
* 6/3/10
* - don't im_invalidate() after paint, this now needs to be at a higher
* level
* 27/9/10
* - use Draw base class
*/
/*
@ -65,6 +67,8 @@
#include <vips/vips.h>
#include "draw.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
@ -105,12 +109,12 @@ typedef struct _Buffer {
/* Our state.
*/
typedef struct {
Draw draw;
/* Parameters.
*/
IMAGE *test; /* Test this image */
IMAGE *mark; /* Mark this image */
int x, y;
PEL *ink; /* Copy of ink param */
Rect *dout; /* Write dirty here at end */
/* Derived stuff.
@ -118,7 +122,6 @@ typedef struct {
PEL *edge; /* Boundary colour */
int equal; /* Fill to == edge, or != edge */
int tsize; /* sizeof( one pel in test ) */
int msize; /* sizeof( one pel in mark ) */
int left, right; /* Record bounding box of modified pixels */
int top, bottom;
@ -208,48 +211,22 @@ flood_connected( Flood *flood, PEL *tp )
return( flood->equal ^ (j < flood->tsize) );
}
/* Is p painted?
*/
static inline gboolean
flood_painted( Flood *flood, PEL *mp )
{
int j;
for( j = 0; j < flood->msize; j++ )
if( mp[j] != flood->ink[j] )
break;
return( j == flood->msize );
}
/* Faster than memcpy for n < about 20.
*/
static inline void
flood_paint( Flood *flood, PEL *q )
{
int j;
for( j = 0; j < flood->msize; j++ )
q[j] = flood->ink[j];
}
/* Fill left and right, return the endpoints. The start point (x, y) must be
* connected and unpainted.
*/
static void
flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
{
const int width = flood->mark->Xsize;
Draw *draw = DRAW( flood );
const int width = flood->test->Xsize;
PEL *tp;
PEL *mp;
int i;
int len;
g_assert( flood_connected( flood,
(PEL *) IM_IMAGE_ADDR( flood->test, x, y ) ) );
g_assert( !flood_painted( flood,
(PEL *) IM_IMAGE_ADDR( flood->mark, x, y ) ) );
g_assert( !im__draw_painted( draw,
(PEL *) IM_IMAGE_ADDR( draw->im, x, y ) ) );
/* Search to the right for the first non-connected pixel. If the start
* pixel is unpainted, we know all the intervening pixels must be
@ -275,12 +252,7 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
/* Paint the range we discovered.
*/
mp = (PEL *) IM_IMAGE_ADDR( flood->mark, *x1, y );
len = *x2 - *x1 + 1;
for( i = 0; i < len; i++ ) {
flood_paint( flood, mp );
mp += flood->msize;
}
im__draw_scanline( DRAW( flood ), y, *x1, *x2 );
if( flood->dout ) {
flood->left = IM_MIN( flood->left, *x1 );
@ -296,6 +268,8 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
static void
flood_around( Flood *flood, Scan *scan )
{
Draw *draw = DRAW( flood );
PEL *tp;
int x;
@ -313,11 +287,11 @@ flood_around( Flood *flood, Scan *scan )
* to check for painted. Otherwise we can get stuck in
* connected loops.
*/
if( flood->mark != flood->test ) {
if( draw->im != flood->test ) {
PEL *mp = (PEL *) IM_IMAGE_ADDR(
flood->mark, x, scan->y );
draw->im, x, scan->y );
if( flood_painted( flood, mp ) )
if( im__draw_painted( draw, mp ) )
continue;
}
@ -388,7 +362,7 @@ flood_free( Flood *flood )
flood->dout->height = flood->bottom - flood->top + 1;
}
IM_FREE( flood->ink );
im__draw_free( DRAW( flood ) );
IM_FREE( flood->edge );
IM_FREEF( buffer_free, flood->in );
IM_FREEF( buffer_free, flood->out );
@ -402,15 +376,17 @@ flood_new( IMAGE *test, IMAGE *mark, int x, int y, PEL *ink, Rect *dout )
if( !(flood = IM_NEW( NULL, Flood )) )
return( NULL );
if( !im__draw_init( DRAW( flood ), mark, ink ) ) {
flood_free( flood );
return( NULL );
}
flood->test = test;
flood->mark = mark;
flood->x = x;
flood->y = y;
flood->ink = NULL;
flood->dout = dout;
flood->edge = NULL;
flood->tsize = IM_IMAGE_SIZEOF_PEL( test );
flood->msize = IM_IMAGE_SIZEOF_PEL( mark );
flood->left = x;
flood->top = y;
flood->right = x;
@ -419,14 +395,12 @@ flood_new( IMAGE *test, IMAGE *mark, int x, int y, PEL *ink, Rect *dout )
flood->in = NULL;
flood->out = NULL;
if( !(flood->ink = (PEL *) im_malloc( NULL, flood->msize )) ||
!(flood->edge = (PEL *) im_malloc( NULL, flood->tsize )) ||
if( !(flood->edge = (PEL *) im_malloc( NULL, flood->tsize )) ||
!(flood->in = buffer_build()) ||
!(flood->out = buffer_build()) ) {
flood_free( flood );
return( NULL );
}
memcpy( flood->ink, ink, flood->msize );
return( flood );
}
@ -459,10 +433,8 @@ im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
{
Flood *flood;
if( im_rwcheck( im ) ||
im_check_coding_known( "im_flood", im ) )
return( -1 );
if( !(flood = flood_new( im, im, x, y, ink, dout )) )
if( im_check_coding_known( "im_flood", im ) ||
!(flood = flood_new( im, im, x, y, ink, dout )) )
return( -1 );
/* Flood to != ink.
@ -506,10 +478,8 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
Flood *flood;
int j;
if( im_rwcheck( im ) ||
im_check_coding_known( "im_flood", im ) )
return( -1 );
if( !(flood = flood_new( im, im, x, y, ink, dout )) )
if( im_check_coding_known( "im_flood", im ) ||
!(flood = flood_new( im, im, x, y, ink, dout )) )
return( -1 );
/* Edge is set by colour of start pixel.
@ -521,7 +491,7 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
* do.
*/
for( j = 0; j < flood->tsize; j++ )
if( flood->edge[j] != flood->ink[j] )
if( flood->edge[j] != DRAW( flood )->ink[j] )
break;
if( j == flood->tsize )
return( 0 );
@ -565,7 +535,6 @@ im_flood_other( IMAGE *test, IMAGE *mark, int x, int y, int serial, Rect *dout )
Flood *flood;
if( im_incheck( test ) ||
im_rwcheck( mark ) ||
im_check_coding_known( "im_flood_other", test ) ||
im_check_uncoded( "im_flood_other", mark ) ||
im_check_mono( "im_flood_other", mark ) ||

View File

@ -15,6 +15,8 @@
* 18/8/10
* - gtkdoc
* - rewritten: clips, fills, any bands, any format
* 27/9/10
* - break base out to Draw
*/
/*
@ -52,6 +54,8 @@
#include <vips/vips.h>
#include "draw.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
@ -59,124 +63,66 @@
/* Our state.
*/
typedef struct {
Draw draw;
/* Parameters.
*/
IMAGE *im; /* Test this image */
int cx, cy;
int radius;
gboolean fill;
PEL *ink; /* Copy of ink param */
/* Derived stuff.
*/
size_t lsize;
size_t psize;
PEL *centre;
/* If the circle is entirely within the image, we have a faster
* noclip path.
*/
gboolean noclip;
} Circle;
/* Faster than memcpy for n < about 20.
*/
static inline void
circle_paint_pel( Circle *circle, PEL *q )
{
int j;
for( j = 0; j < circle->psize; j++ )
q[j] = circle->ink[j];
}
/* Paint, with clip.
*/
static void
circle_paint_pel_clip( Circle *circle, int x, int y )
{
if( x < 0 || x >= circle->im->Xsize )
return;
if( y < 0 || y >= circle->im->Ysize )
return;
circle_paint_pel( circle, (PEL *) IM_IMAGE_ADDR( circle->im, x, y ) );
}
/* Fill a scanline between points x1 and x2 inclusive. x1 < x2.
*/
static void
circle_paint_scanline( Circle *circle, int y, int x1, int x2 )
{
PEL *mp;
int i;
int len;
g_assert( x1 <= x2 );
if( y < 0 || y > circle->im->Ysize )
return;
if( x1 < 0 && x2 < 0 )
return;
if( x1 > circle->im->Xsize && x2 > circle->im->Xsize )
return;
x1 = IM_CLIP( 0, x1, circle->im->Xsize - 1 );
x2 = IM_CLIP( 0, x2, circle->im->Xsize - 1 );
mp = (PEL *) IM_IMAGE_ADDR( circle->im, x1, y );
len = x2 - x1 + 1;
for( i = 0; i < len; i++ ) {
circle_paint_pel( circle, mp );
mp += circle->psize;
}
}
static void
circle_octants( Circle *circle, int x, int y )
{
Draw *draw = DRAW( circle );
if( circle->fill ) {
const int cx = circle->cx;
const int cy = circle->cy;
circle_paint_scanline( circle, cy + y, cx - x, cx + x );
circle_paint_scanline( circle, cy - y, cx - x, cx + x );
circle_paint_scanline( circle, cy + x, cx - y, cx + y );
circle_paint_scanline( circle, cy - x, cx - y, cx + y );
im__draw_scanline( draw, cy + y, cx - x, cx + x );
im__draw_scanline( draw, cy - y, cx - x, cx + x );
im__draw_scanline( draw, cy + x, cx - y, cx + y );
im__draw_scanline( draw, cy - x, cx - y, cx + y );
}
else if( circle->noclip ) {
const size_t lsize = circle->lsize;
const size_t psize = circle->psize;
else if( DRAW( circle )->noclip ) {
const size_t lsize = draw->lsize;
const size_t psize = draw->psize;
PEL *centre = circle->centre;
circle_paint_pel( circle, centre + lsize * y - psize * x );
circle_paint_pel( circle, centre + lsize * y + psize * x );
circle_paint_pel( circle, centre - lsize * y - psize * x );
circle_paint_pel( circle, centre - lsize * y + psize * x );
circle_paint_pel( circle, centre + lsize * x - psize * y );
circle_paint_pel( circle, centre + lsize * x + psize * y );
circle_paint_pel( circle, centre - lsize * x - psize * y );
circle_paint_pel( circle, centre - lsize * x + psize * y );
im__draw_pel( draw, centre + lsize * y - psize * x );
im__draw_pel( draw, centre + lsize * y + psize * x );
im__draw_pel( draw, centre - lsize * y - psize * x );
im__draw_pel( draw, centre - lsize * y + psize * x );
im__draw_pel( draw, centre + lsize * x - psize * y );
im__draw_pel( draw, centre + lsize * x + psize * y );
im__draw_pel( draw, centre - lsize * x - psize * y );
im__draw_pel( draw, centre - lsize * x + psize * y );
}
else {
const int cx = circle->cx;
const int cy = circle->cy;
circle_paint_pel_clip( circle, cx + y, cy - x );
circle_paint_pel_clip( circle, cx + y, cy + x );
circle_paint_pel_clip( circle, cx - y, cy - x );
circle_paint_pel_clip( circle, cx - y, cy + x );
circle_paint_pel_clip( circle, cx + x, cy - y );
circle_paint_pel_clip( circle, cx + x, cy + y );
circle_paint_pel_clip( circle, cx - x, cy - y );
circle_paint_pel_clip( circle, cx - x, cy + y );
im__draw_pel_clip( draw, cx + y, cy - x );
im__draw_pel_clip( draw, cx + y, cy + x );
im__draw_pel_clip( draw, cx - y, cy - x );
im__draw_pel_clip( draw, cx - y, cy + x );
im__draw_pel_clip( draw, cx + x, cy - y );
im__draw_pel_clip( draw, cx + x, cy + y );
im__draw_pel_clip( draw, cx - x, cy - y );
im__draw_pel_clip( draw, cx - x, cy + y );
}
}
static void
circle_free( Circle *circle )
{
IM_FREE( circle->ink );
im__draw_free( DRAW( circle ) );
im_free( circle );
}
@ -187,33 +133,26 @@ circle_new( IMAGE *im, int cx, int cy, int radius, gboolean fill, PEL *ink )
if( !(circle = IM_NEW( NULL, Circle )) )
return( NULL );
circle->im = im;
if( !im__draw_init( DRAW( circle ), im, ink ) ) {
circle_free( circle );
return( NULL );
}
circle->cx = cx;
circle->cy = cy;
circle->radius = radius;
circle->fill = fill;
circle->ink = NULL;
circle->lsize = IM_IMAGE_SIZEOF_LINE( im );
circle->psize = IM_IMAGE_SIZEOF_PEL( im );
circle->centre = (PEL *) IM_IMAGE_ADDR( im, cx, cy );
circle->noclip = FALSE;
if( !(circle->ink = (PEL *) im_malloc( NULL, circle->psize )) ) {
circle_free( circle );
return( NULL );
}
memcpy( circle->ink, ink, circle->psize );
if( cx - radius >= 0 && cx + radius < im->Xsize &&
cy - radius >= 0 && cy + radius < im->Ysize )
circle->noclip = TRUE;
DRAW( circle )->noclip = TRUE;
return( circle );
}
static void
circle_paint( Circle *circle )
circle_draw( Circle *circle )
{
int x, y, d;
@ -267,11 +206,10 @@ im_draw_circle( IMAGE *im, int cx, int cy, int radius, gboolean fill, PEL *ink )
cy + radius < 0 || cy - radius >= im->Ysize )
return( 0 );
if( im_rwcheck( im ) ||
im_check_coding_known( "im_draw_circle", im ) ||
if( im_check_coding_known( "im_draw_circle", im ) ||
!(circle = circle_new( im, cx, cy, radius, fill, ink )) )
return( -1 );
circle_paint( circle );
circle_draw( circle );
circle_free( circle );
return( 0 );

View File

@ -0,0 +1,423 @@
/* draw straight lines
*
* Copyright: J. Cupitt
* Written: 15/06/1992
* Modified : 22/10/92 - clipping constraints changed
* 22/7/93 JC
* - im_incheck() added
* 16/8/94 JC
* - im_incheck() changed to im_makerw()
* 5/12/06
* - im_invalidate() after paint
* 1/3/10
* - oops, lineset needs to ask for WIO of mask and ink
* 6/3/10
* - don't im_invalidate() after paint, this now needs to be at a higher
* level
* 27/9/10
* - gtk-doc
* - use draw.c base class
* - do pointwise clipping
* - rename as im_draw_line() for consistency
* - cleanups!
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include "draw.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
#define SWAP(A,B) {int t; t = (A); (A) = (B); (B) = t;}
typedef struct _Line {
Draw draw;
int x1, y1;
int x2, y2;
int dx;
int dy;
VipsPlotFn plot;
void *a;
void *b;
void *c;
} Line;
static void
line_free( Line *line )
{
im__draw_free( DRAW( line ) );
im_free( line );
}
static Line *
line_new( VipsImage *im, int x1, int y1, int x2, int y2, PEL *ink )
{
Line *line;
if( !(line = IM_NEW( NULL, Line )) )
return( NULL );
if( !im__draw_init( DRAW( line ), im, ink ) ) {
line_free( line );
return( NULL );
}
/* Find offsets.
*/
line->dx = x2 - x1;
line->dy = y2 - y1;
/* Swap endpoints to reduce number of cases.
*/
if( abs( line->dx ) >= abs( line->dy ) && line->dx < 0 ) {
/* Swap to get all x greater or equal cases going to the
* right. Do diagonals here .. just have up and right and down
* and right now.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
else if( abs( line->dx ) < abs( line->dy ) && line->dy < 0 ) {
/* Swap to get all y greater cases going down the screen.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
/* Recalculate dx, dy.
*/
line->dx = x2 - x1;
line->dy = y2 - y1;
line->x1 = x1;
line->y1 = y1;
line->x2 = x2;
line->y2 = y2;
line->plot = NULL;
line->a = NULL;
line->b = NULL;
line->c = NULL;
if( x1 < im->Xsize && x1 >= 0 &&
x2 < im->Xsize && x2 >= 0 &&
y1 < im->Ysize && y1 >= 0 &&
y2 < im->Ysize && y2 >= 0 )
DRAW( line )->noclip = TRUE;
return( line );
}
static inline int
line_plot( Line *line, int x, int y )
{
return( line->plot( DRAW( line )->im, x, y,
line->a, line->b, line->c ) );
}
static int
line_draw( Line *line )
{
int x, y, err;
/* Start point and offset.
*/
x = line->x1;
y = line->y1;
/* Special case: zero width and height is single point.
*/
if( line->dx == 0 && line->dy == 0 ) {
if( line_plot( line, x, y ) )
return( -1 );
}
/* Special case vertical and horizontal lines for speed.
*/
else if( line->dx == 0 ) {
/* Vertical line going down.
*/
for( ; y <= line->y2; y++ ) {
if( line_plot( line, x, y ) )
return( -1 );
}
}
else if( line->dy == 0 ) {
/* Horizontal line to the right.
*/
for( ; x <= line->x2; x++ ) {
if( line_plot( line, x, y ) )
return( -1 );
}
}
/* Special case diagonal lines.
*/
else if( abs( line->dy ) == abs( line->dx ) && line->dy > 0 ) {
/* Diagonal line going down and right.
*/
for( ; x <= line->x2; x++, y++ ) {
if( line_plot( line, x, y ) )
return( -1 );
}
}
else if( abs( line->dy ) == abs( line->dx ) && line->dy < 0 ) {
/* Diagonal line going up and right.
*/
for( ; x <= line->x2; x++, y-- ) {
if( line_plot( line, x, y ) )
return( -1 );
}
}
else if( abs( line->dy ) < abs( line->dx ) && line->dy > 0 ) {
/* Between -45 and 0 degrees.
*/
for( err = 0; x <= line->x2; x++ ) {
if( line_plot( line, x, y ) )
return( -1 );
err += line->dy;
if( err >= line->dx ) {
err -= line->dx;
y++;
}
}
}
else if( abs( line->dy ) < abs( line->dx ) && line->dy < 0 ) {
/* Between 0 and 45 degrees.
*/
for( err = 0; x <= line->x2; x++ ) {
if( line_plot( line, x, y ) )
return( -1 );
err -= line->dy;
if( err >= line->dx ) {
err -= line->dx;
y--;
}
}
}
else if( abs( line->dy ) > abs( line->dx ) && line->dx > 0 ) {
/* Between -45 and -90 degrees.
*/
for( err = 0; y <= line->y2; y++ ) {
if( line_plot( line, x, y ) )
return( -1 );
err += line->dx;
if( err >= line->dy ) {
err -= line->dy;
x++;
}
}
}
else if( abs( line->dy ) > abs( line->dx ) && line->dx < 0 ) {
/* Between -90 and -135 degrees.
*/
for( err = 0; y <= line->y2; y++ ) {
if( line_plot( line, x, y ) )
return( -1 );
err -= line->dx;
if( err >= line->dy ) {
err -= line->dy;
x--;
}
}
}
else
g_assert( 0 );
return( 0 );
}
/**
* im_draw_line_user:
* @im: image to draw on
* @x1: start point
* @y1: start point
* @x2: end point
* @y2: end point
* @ink: value to draw
*
* Draws a 1-pixel-wide line on an image. @x1, @y1 and @x2, @y2 must be
* within the image.
*
* @ink is an array of bytes
* containing a valid pixel for the image's format.
* It must have at least IM_IMAGE_SIZEOF_PEL( @im ) bytes.
*
* See also: im_draw_circle().
*
* Returns: 0 on success, or -1 on error.
*/
/* Draw a line on a image with a user plot function. We do no clipping: the
* user function should check ranges for each pixel when it is called.
*/
int
im_draw_line_user( VipsImage *im,
int x1, int y1, int x2, int y2,
VipsPlotFn plot, void *a, void *b, void *c )
{
Line *line;
if( im_check_coding_known( "im_draw_line", im ) ||
!(line = line_new( im, x1, y1, x2, y2, NULL )) )
return( -1 );
line->plot = plot;
line->a = a;
line->b = b;
line->c = c;
if( line_draw( line ) ) {
line_free( line );
return( -1 );
}
line_free( line );
return( 0 );
}
static int
line_plot_point( VipsImage *im, int x, int y,
void *a, void *b, void *c )
{
Draw *draw = (Draw *) a;
if( draw->noclip )
im__draw_pel( draw, (PEL *) IM_IMAGE_ADDR( draw->im, x, y ) );
else
im__draw_pel_clip( draw, x, y );
return( 0 );
}
/**
* im_draw_line:
* @im: image to draw on
* @x1: start point
* @y1: start point
* @x2: end point
* @y2: end point
* @ink: value to draw
*
* Draws a 1-pixel-wide line on an image.
*
* @ink is an array of bytes
* containing a valid pixel for the image's format.
* It must have at least IM_IMAGE_SIZEOF_PEL( @im ) bytes.
*
* See also: im_draw_circle().
*
* Returns: 0 on success, or -1 on error.
*/
int
im_draw_line( VipsImage *im, int x1, int y1, int x2, int y2, PEL *ink )
{
Line *line;
if( im_check_coding_known( "im_draw_line", im ) ||
!(line = line_new( im, x1, y1, x2, y2, ink )) )
return( -1 );
line->plot = line_plot_point;
line->a = line;
if( line_draw( line ) ) {
line_free( line );
return( 0 );
}
line_free( line );
return( 0 );
}
/* Draw a set of lines with an ink and a mask. A non-inplace operation, handy
* for nip2.
*/
int
im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
int n, int *x1v, int *y1v, int *x2v, int *y2v )
{
Rect mask_rect;
int i;
if( mask->Bands != 1 || mask->BandFmt != IM_BANDFMT_UCHAR ||
mask->Coding != IM_CODING_NONE ) {
im_error( "im_lineset",
"%s", _( "mask image not 1 band 8 bit uncoded" ) );
return( -1 );
}
if( ink->Bands != in->Bands || ink->BandFmt != in->BandFmt ||
ink->Coding != in->Coding ) {
im_error( "im_lineset",
"%s", _( "ink image does not match in image" ) );
return( -1 );
}
if( ink->Xsize != 1 || ink->Ysize != 1 ) {
im_error( "im_lineset", "%s", _( "ink image not 1x1 pixels" ) );
return( -1 );
}
/* Copy the image then fastline to it ... this will render to a "t"
* usually.
*/
if( im_copy( in, out ) )
return( -1 );
mask_rect.left = mask->Xsize / 2;
mask_rect.top = mask->Ysize / 2;
mask_rect.width = mask->Xsize;
mask_rect.height = mask->Ysize;
if( im_incheck( ink ) ||
im_incheck( mask ) )
return( -1 );
for( i = 0; i < n; i++ ) {
if( im_fastlineuser( out, x1v[i], y1v[i], x2v[i], y2v[i],
im_plotmask, ink->data, mask->data, &mask_rect ) )
return( -1 );
}
return( 0 );
}

View File

@ -15,6 +15,8 @@
* - gtk-doc
* - added 'fill'
* - renamed as im_draw_rect() for consistency
* 27/9/10
* - memcpy() subsequent lines of the rect
*/
/*
@ -50,6 +52,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
@ -118,20 +121,22 @@ im_draw_rect( IMAGE *im,
if( im_rect_isempty( &clipped ) )
return( 0 );
/* Loop through image plotting where required.
/* We plot the first line pointwise, then memcpy() it for the
* subsequent lines.
*/
to = (PEL *) IM_IMAGE_ADDR( im, clipped.left, clipped.top );
for( y = 0; y < clipped.height; y++ ) {
q = to;
for( x = 0; x < clipped.width; x++ ) {
for( b = 0; b < ps; b++ )
q[b] = ink[b];
q = to;
for( x = 0; x < clipped.width; x++ )
for( b = 0; b < ps; b++ ) {
q[b] = ink[b];
q += ps;
}
to += ls;
q = to + ls;
for( y = 1; y < clipped.height; y++ ) {
memcpy( q, to, clipped.width * ps );
q += ls;
}
return( 0 );

View File

@ -49,8 +49,12 @@
* @stability: Stable
* @include: vips/vips.h
*
* These operations modify the input image. You can't easily use them in
* pipelines, but they are useful for paintbox-style programs.
* These operations directly modify the image. They do not thread, on 32-bit
* machines they will be limited to 2GB images, and a little care needs to be
* taken if you use them as part of an image pipeline.
*
* They are mostly supposed to be useful
* for paintbox-style programs.
*
*/

View File

@ -1,478 +0,0 @@
/* @(#) Line drawer. Faster than the old im_line. Any number of bands,
* @(#) any type including complex. Instead of passing a PEL value, pass a
* @(#) pointer to the pel value you wish to plot. The correct number of
* @(#) bytes must be there! Both start and end points should be in the
* @(#) image.
* @(#)
* @(#) int
* @(#) im_fastline( im, x1, y1, x2, y2, pel )
* @(#) IMAGE *im;
* @(#) int x1, x2, y1, y2;
* @(#) PEL *pel;
* @(#)
* @(#) As above, but rather than plotting a point, call a passed function
* @(#) for every point on the line. Up to three extra args passed down too.
* @(#) If the passed function returns non-zero, im_fastlineuser stops and
* @(#) returns non-zero. Start and end points may be outside the image -
* @(#) clipping is the responsibility of the user function.
* @(#)
* @(#) int
* @(#) im_fastlineuser( im, x1, y1, x2, y2, plot_fn,
* @(#) client1, client2, client3 )
* @(#) IMAGE *im;
* @(#) int x1, x2, y1, y2;
* @(#) int (*plot_fn)();
* @(#) void *client1, *client2, *client3;
* @(#)
* @(#) int
* @(#) plot_fn( im, x, y, client1, client2, client3 )
* @(#) IMAGE *im;
* @(#) int x, y;
* @(#) void *client1, *client2, *client3;
* @(#)
*
* Copyright: J. Cupitt
* Written: 15/06/1992
* Modified : 22/10/92 - clipping constraints changed
* 22/7/93 JC
* - im_incheck() added
* 16/8/94 JC
* - im_incheck() changed to im_makerw()
* 5/12/06
* - im_invalidate() after paint
* 1/3/10
* - oops, lineset needs to ask for WIO of mask and ink
* 6/3/10
* - don't im_invalidate() after paint, this now needs to be at a higher
* level
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
#define SWAP(A,B) {int t; t = (A); (A) = (B); (B) = t;}
/* Draw a line on a image.
*/
/**
* im_draw_line:
* @im: image to draw on
* @x1: start point
* @y1: start point
* @x2: end point
* @y2: end point
* @ink: value to draw
*
* Draws a 1-pixel-wide line on an image. @x1, @y1 and @x2, @y2 must be
* within the image.
*
* @ink is an array of bytes
* containing a valid pixel for the image's format.
* It must have at least IM_IMAGE_SIZEOF_PEL( @im ) bytes.
*
* This an inplace operation, so @im is changed. It does not thread and will
* not work well as part of a pipeline. On 32-bit machines it will be limited
* to 2GB images.
*
* See also: im_draw_circle().
*
* Returns: 0 on success, or -1 on error.
*/
int
im_draw_line( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel )
{
int es = IM_IMAGE_SIZEOF_ELEMENT( im );
int ps = es * im->Bands;
int ls = ps * im->Xsize;
PEL *p;
int x, y, dx, dy;
int err;
int b;
if( im_rwcheck( im ) )
return( -1 );
/* Check coordinates in range.
*/
if( x1 > im->Xsize || x1 < 0 ||
y1 > im->Ysize || y1 < 0 ||
x2 > im->Xsize || x2 < 0 ||
y2 > im->Ysize || y2 < 0 ) {
im_error( "im_fastline", "%s",
_( "invalid line cooordinates" ) );
return( -1 );
}
/* Find offsets.
*/
dx = x2 - x1;
dy = y2 - y1;
/* Swap endpoints to reduce number of cases.
*/
if( abs( dx ) >= abs( dy ) && dx < 0 ) {
/* Swap to get all x greater or equal cases going to the
* right. Do diagonals here .. just have up and right and down
* and right now.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
else if( abs( dx ) < abs( dy ) && dy < 0 ) {
/* Swap to get all y greater cases going down the screen.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
/* Recalculate dx, dy.
*/
dx = x2 - x1;
dy = y2 - y1;
/* Start point and offset.
*/
x = x1;
y = y1;
p = (PEL *) im->data + x * ps + y * ls;
/* Plot point macro.
*/
#define PLOT \
for( b = 0; b < ps; b++ ) \
p[b] = pel[b];
/* Special case: zero width and height is single point.
*/
if( dx == 0 && dy == 0 ) {
PLOT;
}
/* Special case vertical and horizontal lines for speed.
*/
else if( dx == 0 ) {
/* Vertical line going down.
*/
for( ; y <= y2; y++ ) {
PLOT;
p += ls;
}
}
else if( dy == 0 ) {
/* Horizontal line to the right.
*/
for( ; x <= x2; x++ ) {
PLOT;
p += ps;
}
}
/* Special case diagonal lines.
*/
else if( abs( dy ) == abs( dx ) && dy > 0 ) {
/* Diagonal line going down and right.
*/
for( ; x <= x2; x++ ) {
PLOT;
p += ps + ls;
}
}
else if( abs( dy ) == abs( dx ) && dy < 0 ) {
/* Diagonal line going up and right.
*/
for( ; x <= x2; x++ ) {
PLOT;
p += ps - ls;
}
}
else if( abs( dy ) < abs( dx ) && dy > 0 ) {
/* Between -45 and 0 degrees.
*/
for( err = 0; x <= x2; x++ ) {
PLOT;
p += ps;
err += dy;
if( err >= dx ) {
err -= dx;
p += ls;
}
}
}
else if( abs( dy ) < abs( dx ) && dy < 0 ) {
/* Between 0 and 45 degrees.
*/
for( err = 0; x <= x2; x++ ) {
PLOT;
p += ps;
err -= dy;
if( err >= dx ) {
err -= dx;
p -= ls;
}
}
}
else if( abs( dy ) > abs( dx ) && dx > 0 ) {
/* Between -45 and -90 degrees.
*/
for( err = 0; y <= y2; y++ ) {
PLOT;
p += ls;
err += dx;
if( err >= dy ) {
err -= dy;
p += ps;
}
}
}
else if( abs( dy ) > abs( dx ) && dx < 0 ) {
/* Between -90 and -135 degrees.
*/
for( err = 0; y <= y2; y++ ) {
PLOT;
p += ls;
err -= dx;
if( err >= dy ) {
err -= dy;
p -= ps;
}
}
}
else
error_exit( "internal error #9872659823475982375" );
return( 0 );
}
/* Draw a line on a image with a user plot function. We do no clipping: the
* user function should check ranges for each pixel when it is called.
*/
int
im_fastlineuser( IMAGE *im,
int x1, int y1, int x2, int y2,
int (*fn)(), void *client1, void *client2, void *client3 )
{
int x, y, dx, dy;
int err;
if( im_rwcheck( im ) )
return( -1 );
/* Find offsets.
*/
dx = x2 - x1;
dy = y2 - y1;
/* Swap endpoints to reduce number of cases.
*/
if( abs( dx ) >= abs( dy ) && dx < 0 ) {
/* Swap to get all x greater or equal cases going to the
* right. Do diagonals here .. just have up and right and down
* and right now.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
else if( abs( dx ) < abs( dy ) && dy < 0 ) {
/* Swap to get all y greater cases going down the screen.
*/
SWAP( x1, x2 );
SWAP( y1, y2 );
}
/* Recalculate dx, dy.
*/
dx = x2 - x1;
dy = y2 - y1;
/* Start point and offset.
*/
x = x1;
y = y1;
/* Special case: zero width and height is single point.
*/
if( dx == 0 && dy == 0 ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
}
/* Special case vertical and horizontal lines for speed.
*/
else if( dx == 0 ) {
/* Vertical line going down.
*/
for( ; y <= y2; y++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
}
}
else if( dy == 0 ) {
/* Horizontal line to the right.
*/
for( ; x <= x2; x++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
}
}
/* Special case diagonal lines.
*/
else if( abs( dy ) == abs( dx ) && dy > 0 ) {
/* Diagonal line going down and right.
*/
for( ; x <= x2; x++, y++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
}
}
else if( abs( dy ) == abs( dx ) && dy < 0 ) {
/* Diagonal line going up and right.
*/
for( ; x <= x2; x++, y-- ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
}
}
else if( abs( dy ) < abs( dx ) && dy > 0 ) {
/* Between -45 and 0 degrees.
*/
for( err = 0; x <= x2; x++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
err += dy;
if( err >= dx ) {
err -= dx;
y++;
}
}
}
else if( abs( dy ) < abs( dx ) && dy < 0 ) {
/* Between 0 and 45 degrees.
*/
for( err = 0; x <= x2; x++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
err -= dy;
if( err >= dx ) {
err -= dx;
y--;
}
}
}
else if( abs( dy ) > abs( dx ) && dx > 0 ) {
/* Between -45 and -90 degrees.
*/
for( err = 0; y <= y2; y++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
err += dx;
if( err >= dy ) {
err -= dy;
x++;
}
}
}
else if( abs( dy ) > abs( dx ) && dx < 0 ) {
/* Between -90 and -135 degrees.
*/
for( err = 0; y <= y2; y++ ) {
if( fn( im, x, y, client1, client2, client3 ) )
return( 1 );
err -= dx;
if( err >= dy ) {
err -= dy;
x--;
}
}
}
else
error_exit( "internal error #9872659823475982375" );
return( 0 );
}
/* Draw a set of lines with an ink and a mask. A non-inplace operation, handy
* for nip2.
*/
int
im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
int n, int *x1v, int *y1v, int *x2v, int *y2v )
{
Rect mask_rect;
int i;
if( mask->Bands != 1 || mask->BandFmt != IM_BANDFMT_UCHAR ||
mask->Coding != IM_CODING_NONE ) {
im_error( "im_lineset",
"%s", _( "mask image not 1 band 8 bit uncoded" ) );
return( -1 );
}
if( ink->Bands != in->Bands || ink->BandFmt != in->BandFmt ||
ink->Coding != in->Coding ) {
im_error( "im_lineset",
"%s", _( "ink image does not match in image" ) );
return( -1 );
}
if( ink->Xsize != 1 || ink->Ysize != 1 ) {
im_error( "im_lineset", "%s", _( "ink image not 1x1 pixels" ) );
return( -1 );
}
/* Copy the image then fastline to it ... this will render to a "t"
* usually.
*/
if( im_copy( in, out ) )
return( -1 );
mask_rect.left = mask->Xsize / 2;
mask_rect.top = mask->Ysize / 2;
mask_rect.width = mask->Xsize;
mask_rect.height = mask->Ysize;
if( im_incheck( ink ) ||
im_incheck( mask ) )
return( -1 );
for( i = 0; i < n; i++ ) {
if( im_fastlineuser( out, x1v[i], y1v[i], x2v[i], y2v[i],
im_plotmask, ink->data, mask->data, &mask_rect ) )
return( -1 );
}
return( 0 );
}