]> git.imager.perl.org - imager.git/blobdiff - draw.c
allow Imager to be loaded on Windows 98
[imager.git] / draw.c
diff --git a/draw.c b/draw.c
index 6cca0a002d7df71f61a98fa55636c774e03e1fa2..8cbe445d26b806cc54ff76d677622cf6d1216542 100644 (file)
--- a/draw.c
+++ b/draw.c
@@ -1,10 +1,14 @@
-#include "image.h"
+#include "imager.h"
 #include "draw.h"
 #include "log.h"
-#include "imagei.h"
+#include "imageri.h"
 
 #include <limits.h>
 
+static void
+cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm, 
+              int bxmin, int bxmax, int bymin, int bymax);
+
 void
 i_mmarray_cr(i_mmarray *ar,int l) {
   int i;
@@ -201,8 +205,20 @@ i_arc_hlines(i_int_hlines *hlines,int x,int y,float rad,float d1,float d2) {
   }
 }
 
+/*
+=item i_arc(im, x, y, rad, d1, d2, color)
+
+=category Drawing
+=synopsis i_arc(im, 50, 50, 20, 45, 135, &color);
+
+Fills an arc centered at (x,y) with radius I<rad> covering the range
+of angles in degrees from d1 to d2, with the color.
+
+=cut
+*/
+
 void
-i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val) {
+i_arc(i_img *im,int x,int y,float rad,float d1,float d2,const i_color *val) {
   i_int_hlines hlines;
 
   i_int_init_hlines_img(&hlines, im);
@@ -214,6 +230,18 @@ i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val) {
   i_int_hlines_destroy(&hlines);
 }
 
+/*
+=item i_arc_cfill(im, x, y, rad, d1, d2, fill)
+
+=category Drawing
+=synopsis i_arc_cfill(im, 50, 50, 35, 90, 135, fill);
+
+Fills an arc centered at (x,y) with radius I<rad> covering the range
+of angles in degrees from d1 to d2, with the fill object.
+
+=cut
+*/
+
 #define MIN_CIRCLE_STEPS 8
 #define MAX_CIRCLE_STEPS 360
 
@@ -277,8 +305,9 @@ arc_poly(int *count, double **xvals, double **yvals,
   angle_inc = 2 * PI / steps;
 
   point_count = steps + 5; /* rough */
-  *xvals = mymalloc(point_count * sizeof(double));
-  *yvals = mymalloc(point_count * sizeof(double));
+  /* point_count is always relatively small, so allocation won't overflow */
+  *xvals = mymalloc(point_count * sizeof(double)); /* checked 17feb2005 tonyc */
+  *yvals = mymalloc(point_count * sizeof(double)); /* checked 17feb2005 tonyc */
 
   /* from centre to edge at d1 */
   (*xvals)[0] = x;
@@ -301,9 +330,21 @@ arc_poly(int *count, double **xvals, double **yvals,
   ++*count;
 }
 
+/*
+=item i_arc_aa(im, x, y, rad, d1, d2, color)
+
+=category Drawing
+=synopsis i_arc_aa(im, 50, 50, 35, 90, 135, &color);
+
+Antialias fills an arc centered at (x,y) with radius I<rad> covering
+the range of angles in degrees from d1 to d2, with the color.
+
+=cut
+*/
+
 void
 i_arc_aa(i_img *im, double x, double y, double rad, double d1, double d2,
-        i_color *val) {
+        const i_color *val) {
   double *xvals, *yvals;
   int count;
 
@@ -315,6 +356,18 @@ i_arc_aa(i_img *im, double x, double y, double rad, double d1, double d2,
   myfree(yvals);
 }
 
+/*
+=item i_arc_aa_cfill(im, x, y, rad, d1, d2, fill)
+
+=category Drawing
+=synopsis i_arc_aa_cfill(im, 50, 50, 35, 90, 135, fill);
+
+Antialias fills an arc centered at (x,y) with radius I<rad> covering
+the range of angles in degrees from d1 to d2, with the fill object.
+
+=cut
+*/
+
 void
 i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
               i_fill_t *fill) {
@@ -334,9 +387,6 @@ i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
 
 typedef int frac;
 static  frac float_to_frac(float x) { return (frac)(0.5+x*16.0); }
-static   int frac_sub     (frac x)  { return (x%16); }
-static   int frac_int     (frac x)  { return (x/16); }
-static float frac_to_float(float x) { return (float)x/16.0; }
 
 static 
 void
@@ -345,19 +395,6 @@ polar_to_plane(float cx, float cy, float angle, float radius, frac *x, frac *y)
   *y = float_to_frac(cy+radius*sin(angle));
 }
 
-static
-void
-order_pair(frac *x, frac *y) {
-  frac t = *x;
-  if (t>*y) {
-    *x = *y;
-    *y = t;
-  }
-}
-
-
-
-
 static
 void
 make_minmax_list(i_mmarray *dot, float x, float y, float radius) {
@@ -425,8 +462,19 @@ i_pixel_coverage(i_mmarray *dot, int x, int y) {
   return cnt;
 }
 
+/*
+=item i_circle_aa(im, x, y, rad, color)
+
+=category Drawing
+=synopsis i_circle_aa(im, 50, 50, 45, &color);
+
+Antialias fills a circle centered at (x,y) for radius I<rad> with
+color.
+
+=cut
+*/
 void
-i_circle_aa(i_img *im, float x, float y, float rad, i_color *val) {
+i_circle_aa(i_img *im, float x, float y, float rad, const i_color *val) {
   i_mmarray dot;
   i_color temp;
   int ly;
@@ -468,13 +516,19 @@ i_circle_aa(i_img *im, float x, float y, float rad, i_color *val) {
   i_mmarray_dst(&dot);
 }
 
+/*
+=item i_box(im, x1, y1, x2, y2, color)
 
+=category Drawing
+=synopsis i_box(im, 0, 0, im->xsize-1, im->ysize-1, &color).
 
+Outlines the box from (x1,y1) to (x2,y2) inclusive with I<color>.
 
-
+=cut
+*/
 
 void
-i_box(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+i_box(i_img *im,int x1,int y1,int x2,int y2,const i_color *val) {
   int x,y;
   mm_log((1,"i_box(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
   for(x=x1;x<x2+1;x++) {
@@ -487,13 +541,35 @@ i_box(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
   }
 }
 
+/*
+=item i_box_filled(im, x1, y1, x2, y2, color)
+
+=category Drawing
+=synopsis i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &color);
+
+Fills the box from (x1,y1) to (x2,y2) inclusive with color.
+
+=cut
+*/
+
 void
-i_box_filled(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+i_box_filled(i_img *im,int x1,int y1,int x2,int y2, const i_color *val) {
   int x,y;
   mm_log((1,"i_box_filled(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
   for(x=x1;x<x2+1;x++) for (y=y1;y<y2+1;y++) i_ppix(im,x,y,val);
 }
 
+/*
+=item i_box_cfill(im, x1, y1, x2, y2, fill)
+
+=category Drawing
+=synopsis i_box_cfill(im, 0, 0, im->xsize-1, im->ysize-1, fill);
+
+Fills the box from (x1,y1) to (x2,y2) inclusive with fill.
+
+=cut
+*/
+
 void
 i_box_cfill(i_img *im,int x1,int y1,int x2,int y2,i_fill_t *fill) {
   mm_log((1,"i_box_cfill(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,fill 0x%x)\n",im,x1,y1,x2,y2,fill));
@@ -557,6 +633,8 @@ i_box_cfill(i_img *im,int x1,int y1,int x2,int y2,i_fill_t *fill) {
 /* 
 =item i_line(im, x1, y1, x2, y2, val, endp)
 
+=category Drawing
+
 Draw a line to image using bresenhams linedrawing algorithm
 
    im   - image to draw to
@@ -571,7 +649,7 @@ Draw a line to image using bresenhams linedrawing algorithm
 */
 
 void
-i_line(i_img *im, int x1, int y1, int x2, int y2, i_color *val, int endp) {
+i_line(i_img *im, int x1, int y1, int x2, int y2, const i_color *val, int endp) {
   int x, y;
   int dx, dy;
   int p;
@@ -756,10 +834,20 @@ i_line_aa3(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
 }
 
 
+/*
+=item i_line_aa(im, x1, x2, y1, y2, color, endp)
+
+=category Drawing
+
+Antialias draws a line from (x1,y1) to (x2, y2) in color.
+
+The point (x2, y2) is drawn only if endp is set.
 
+=cut
+*/
 
 void
-i_line_aa(i_img *im, int x1, int y1, int x2, int y2, i_color *val, int endp) {
+i_line_aa(i_img *im, int x1, int y1, int x2, int y2, const i_color *val, int endp) {
   int x, y;
   int dx, dy;
   int p;
@@ -903,7 +991,7 @@ perm(int n,int k) {
    to get a new level - this may lead to errors who knows lets test it */
 
 void
-i_bezier_multi(i_img *im,int l,double *x,double *y,i_color *val) {
+i_bezier_multi(i_img *im,int l,const double *x,const double *y, const i_color *val) {
   double *bzcoef;
   double t,cx,cy;
   int k,i;
@@ -1002,33 +1090,46 @@ crdata(int left,int right,int dadl,int dadr,int y, int dir) {
 
 /* i_ccomp compares two colors and gives true if they are the same */
 
+typedef int (*ff_cmpfunc)(i_color const *c1, i_color const *c2, int channels);
+
 static int
-i_ccomp(i_color *val1,i_color *val2,int ch) {
+i_ccomp_normal(i_color const *val1, i_color const *val2, int ch) {
   int i;
-  for(i=0;i<ch;i++) if (val1->channel[i] !=val2->channel[i]) return 0;
+  for(i = 0; i < ch; i++) 
+    if (val1->channel[i] !=val2->channel[i])
+      return 0;
   return 1;
 }
 
+static int
+i_ccomp_border(i_color const *val1, i_color const *val2, int ch) {
+  int i;
+  for(i = 0; i < ch; i++) 
+    if (val1->channel[i] !=val2->channel[i])
+      return 1;
+  return 0;
+}
 
 static int
-i_lspan(i_img *im, int seedx, int seedy, i_color *val) {
+i_lspan(i_img *im, int seedx, int seedy, i_color const *val, ff_cmpfunc cmpfunc) {
   i_color cval;
   while(1) {
     if (seedx-1 < 0) break;
     i_gpix(im,seedx-1,seedy,&cval);
-    if (!i_ccomp(val,&cval,im->channels)) break;
+    if (!cmpfunc(val,&cval,im->channels)) 
+      break;
     seedx--;
   }
   return seedx;
 }
 
 static int
-i_rspan(i_img *im, int seedx, int seedy, i_color *val) {
+i_rspan(i_img *im, int seedx, int seedy, i_color const *val, ff_cmpfunc cmpfunc) {
   i_color cval;
   while(1) {
     if (seedx+1 > im->xsize-1) break;
     i_gpix(im,seedx+1,seedy,&cval);
-    if (!i_ccomp(val,&cval,im->channels)) break;
+    if (!cmpfunc(val,&cval,im->channels)) break;
     seedx++;
   }
   return seedx;
@@ -1068,7 +1169,7 @@ i_rspan(i_img *im, int seedx, int seedy, i_color *val) {
 #define SET(x,y) btm_set(btm,x,y)
 
 /* INSIDE returns true if pixel is correct color and we haven't set it before. */
-#define INSIDE(x,y) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),i_ccomp(&val,&cval,channels)  ) ))
+#define INSIDE(x,y, seed) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),cmpfunc(seed,&cval,channels)  ) ))
 
 
 
@@ -1076,15 +1177,8 @@ i_rspan(i_img *im, int seedx, int seedy, i_color *val) {
 
 static struct i_bitmap *
 i_flood_fill_low(i_img *im,int seedx,int seedy,
-                 int *bxminp, int *bxmaxp, int *byminp, int *bymaxp) {
-
-  /*
-    int lx,rx;
-    int y;
-    int direction;
-    int dadLx,dadRx;
-    int wasIn=0;
-  */
+                 int *bxminp, int *bxmaxp, int *byminp, int *bymaxp,
+                i_color const *seed, ff_cmpfunc cmpfunc) {
   int ltx, rtx;
   int tx = 0;
 
@@ -1097,7 +1191,7 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
   struct i_bitmap *btm;
 
   int channels,xsize,ysize;
-  i_color cval,val;
+  i_color cval;
 
   channels = im->channels;
   xsize    = im->xsize;
@@ -1106,12 +1200,9 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
   btm = btm_new(xsize, ysize);
   st  = llist_new(100, sizeof(struct stack_element*));
 
-  /* Get the reference color */
-  i_gpix(im, seedx, seedy, &val);
-
   /* Find the starting span and fill it */
-  ltx = i_lspan(im, seedx, seedy, &val);
-  rtx = i_rspan(im, seedx, seedy, &val);
+  ltx = i_lspan(im, seedx, seedy, seed, cmpfunc);
+  rtx = i_rspan(im, seedx, seedy, seed, cmpfunc);
   for(tx=ltx; tx<=rtx; tx++) SET(tx, seedy);
 
   ST_PUSH(ltx, rtx, ltx, rtx, seedy+1,  1);
@@ -1136,10 +1227,10 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
 
 
     x = lx+1;
-    if ( lx >= 0 && (wasIn = INSIDE(lx, y)) ) {
+    if ( lx >= 0 && (wasIn = INSIDE(lx, y, seed)) ) {
       SET(lx, y);
       lx--;
-      while(INSIDE(lx, y) && lx > 0) {
+      while(INSIDE(lx, y, seed) && lx > 0) {
        SET(lx,y);
        lx--;
       }
@@ -1150,7 +1241,7 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
       /*  printf("x=%d\n",x); */
       if (wasIn) {
        
-       if (INSIDE(x, y)) {
+       if (INSIDE(x, y, seed)) {
          /* case 1: was inside, am still inside */
          SET(x,y);
        } else {
@@ -1163,7 +1254,7 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
        }
       } else {
        if (x > rx) goto EXT;
-       if (INSIDE(x, y)) {
+       if (INSIDE(x, y, seed)) {
          SET(x, y);
          /* case 3: Wasn't inside, am now: just found the start of a new run */
          wasIn = 1;
@@ -1192,14 +1283,26 @@ i_flood_fill_low(i_img *im,int seedx,int seedy,
   return btm;
 }
 
+/*
+=item i_flood_fill(im, seedx, seedy, color)
+
+=category Drawing
+=synopsis i_flood_fill(im, 50, 50, &color);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<color>.
 
+Returns false if (seedx, seedy) are outside the image.
 
+=cut
+*/
 
 undef_int
-i_flood_fill(i_img *im, int seedx, int seedy, i_color *dcol) {
+i_flood_fill(i_img *im, int seedx, int seedy, const i_color *dcol) {
   int bxmin, bxmax, bymin, bymax;
   struct i_bitmap *btm;
   int x, y;
+  i_color val;
 
   i_clear_error();
   if (seedx < 0 || seedx >= im->xsize ||
@@ -1208,7 +1311,11 @@ i_flood_fill(i_img *im, int seedx, int seedy, i_color *dcol) {
     return 0;
   }
 
-  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax);
+  /* Get the reference color */
+  i_gpix(im, seedx, seedy, &val);
+
+  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+                        &val, i_ccomp_normal);
 
   for(y=bymin;y<=bymax;y++)
     for(x=bxmin;x<=bxmax;x++)
@@ -1218,14 +1325,25 @@ i_flood_fill(i_img *im, int seedx, int seedy, i_color *dcol) {
   return 1;
 }
 
+/*
+=item i_flood_cfill(im, seedx, seedy, fill)
+
+=category Drawing
+=synopsis i_flood_cfill(im, 50, 50, fill);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<fill>.
+
+Returns false if (seedx, seedy) are outside the image.
 
+=cut
+*/
 
 undef_int
 i_flood_cfill(i_img *im, int seedx, int seedy, i_fill_t *fill) {
   int bxmin, bxmax, bymin, bymax;
   struct i_bitmap *btm;
-  int x, y;
-  int start;
+  i_color val;
 
   i_clear_error();
   
@@ -1235,7 +1353,102 @@ i_flood_cfill(i_img *im, int seedx, int seedy, i_fill_t *fill) {
     return 0;
   }
 
-  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax);
+  /* Get the reference color */
+  i_gpix(im, seedx, seedy, &val);
+
+  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+                        &val, i_ccomp_normal);
+
+  cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
+
+  btm_destroy(btm);
+  return 1;
+}
+
+/*
+=item i_flood_fill_border(im, seedx, seedy, color, border)
+
+=category Drawing
+=synopsis i_flood_fill_border(im, 50, 50, &color, &border);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<color>, fill stops when the fill reaches a pixels with
+color I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+=cut
+*/
+
+undef_int
+i_flood_fill_border(i_img *im, int seedx, int seedy, const i_color *dcol,
+                   const i_color *border) {
+  int bxmin, bxmax, bymin, bymax;
+  struct i_bitmap *btm;
+  int x, y;
+
+  i_clear_error();
+  if (seedx < 0 || seedx >= im->xsize ||
+      seedy < 0 || seedy >= im->ysize) {
+    i_push_error(0, "i_flood_cfill: Seed pixel outside of image");
+    return 0;
+  }
+
+  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+                        border, i_ccomp_border);
+
+  for(y=bymin;y<=bymax;y++)
+    for(x=bxmin;x<=bxmax;x++)
+      if (btm_test(btm,x,y)) 
+       i_ppix(im,x,y,dcol);
+  btm_destroy(btm);
+  return 1;
+}
+
+/*
+=item i_flood_cfill_border(im, seedx, seedy, fill, border)
+
+=category Drawing
+=synopsis i_flood_cfill_border(im, 50, 50, fill, border);
+
+Flood fills the 4-connected region starting from the point (seedx,
+seedy) with I<fill>, the fill stops when it reaches pixels of color
+I<border>.
+
+Returns false if (seedx, seedy) are outside the image.
+
+=cut
+*/
+
+undef_int
+i_flood_cfill_border(i_img *im, int seedx, int seedy, i_fill_t *fill,
+                    const i_color *border) {
+  int bxmin, bxmax, bymin, bymax;
+  struct i_bitmap *btm;
+
+  i_clear_error();
+  
+  if (seedx < 0 || seedx >= im->xsize ||
+      seedy < 0 || seedy >= im->ysize) {
+    i_push_error(0, "i_flood_cfill_border: Seed pixel outside of image");
+    return 0;
+  }
+
+  btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
+                        border, i_ccomp_border);
+
+  cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
+
+  btm_destroy(btm);
+
+  return 1;
+}
+
+static void
+cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm, 
+              int bxmin, int bxmax, int bymin, int bymax) {
+  int x, y;
+  int start;
 
   if (im->bits == i_8_bits && fill->fill_with_color) {
     /* bxmax/bxmin are inside the image, hence this won't overflow */
@@ -1309,7 +1522,4 @@ i_flood_cfill(i_img *im, int seedx, int seedy, i_fill_t *fill) {
     if (work)
       myfree(work);
   }
-
-  btm_destroy(btm);
-  return 1;
 }