[perl #101682] define i_circle_aa_fill() and use it circle
authorTony Cook <tony@develop-help.com>
Sat, 24 Jan 2015 03:08:40 +0000 (14:08 +1100)
committerTony Cook <tony@develop-help.com>
Sat, 24 Jan 2015 03:08:40 +0000 (14:08 +1100)
speeds up smaller circle fills considerably, larger circles are
dominated by fill time rather than calculation so lose out

Imager.pm
Imager.xs
bench/circlef.pl
draw.c
imager.h

index e6137be..4ff2d74 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -2894,8 +2894,14 @@ sub arc {
          return;
        }
       }
-      i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
-                    $opts{'d2'}, $opts{fill}{fill});
+      if ($opts{d1} == 0 && $opts{d2} == 361) {
+       i_circle_aa_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'},
+                        $opts{fill}{fill});
+      }
+      else {
+       i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
+                      $opts{'d2'}, $opts{fill}{fill});
+      }
     }
     elsif ($opts{filled}) {
       my $color = _color($opts{'color'});
index 470dffb..6847b02 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -1750,6 +1750,14 @@ i_circle_aa(im,x,y,rad,val)
              double     rad
           Imager::Color    val
 
+void
+i_circle_aa_fill(im,x,y,rad,fill)
+    Imager::ImgRaw     im
+            double     x
+            double     y
+             double     rad
+          Imager::FillHandle    fill
+
 int
 i_circle_out(im,x,y,rad,val)
     Imager::ImgRaw     im
index 077051e..5a39a9c 100644 (file)
@@ -90,3 +90,15 @@ small_c_aa: 166.8 /s (0.005993 / iter)
 small_fill_aa: 9.9 /s (0.101373 / iter)
 tiny_c_aa: 252.1 /s (0.003967 / iter)
 tiny_fill_aa: 25.7 /s (0.038897 / iter)
+
+add i_circle_aa_fill() and use it:
+
+large_c_aa: 1.1 /s (0.948333 / iter)
+large_fill_aa: 0.7 /s (1.500000 / iter)
+medium_c_aa: 31.0 /s (0.032303 / iter)
+medium_fill_aa: 22.1 /s (0.045175 / iter)
+small_c_aa: 223.1 /s (0.004482 / iter)
+small_fill_aa: 218.1 /s (0.004585 / iter)
+tiny_c_aa: 394.4 /s (0.002536 / iter)
+tiny_fill_aa: 437.8 /s (0.002284 / iter)
+
diff --git a/draw.c b/draw.c
index 0aa6bb0..9103c78 100644 (file)
--- a/draw.c
+++ b/draw.c
@@ -424,6 +424,28 @@ i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
 typedef i_img_dim frac;
 static  frac float_to_frac(double x) { return (frac)(0.5+x*16.0); }
 
+typedef void
+(*flush_render_t)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_sample_t *cover, void *ctx);
+
+static void
+i_circle_aa_low(i_img *im, double x, double y, double rad, flush_render_t r, void *ctx);
+
+static void
+scanline_flush_color(i_img *im, i_img_dim l, i_img_dim y, i_img_dim width, const i_sample_t *cover, void *ctx);
+
+static void
+scanline_flush_fill(i_img *im, i_img_dim l, i_img_dim y, i_img_dim width, const i_sample_t *cover, void *ctx);
+
+typedef struct {
+  i_render r;
+  i_color c;
+} flush_color_t;
+
+typedef struct {
+  i_render r;
+  i_fill_t *fill;
+} flush_fill_t;
+
 /*
 =item i_circle_aa(im, x, y, rad, color)
 
@@ -435,21 +457,59 @@ color.
 
 =cut
 */
+
 void
 i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
+  flush_color_t fc;
+
+  fc.c = *val;
+  i_render_init(&fc.r, im, rad * 2 + 1);
+
+  i_circle_aa_low(im, x, y, rad, scanline_flush_color, &fc);
+
+  i_render_done(&fc.r);
+}
+
+/*
+=item i_circle_aa_fill(im, x, y, rad, fill)
+
+=category Drawing
+=synopsis i_circle_aa_fill(im, 50, 50, 45, fill);
+
+Anti-alias fills a circle centered at (x,y) for radius I<rad> with
+fill.
+
+=cut
+*/
+
+void
+i_circle_aa_fill(i_img *im, double x, double y, double rad, i_fill_t *fill) {
+  flush_fill_t ff;
+
+  ff.fill = fill;
+  i_render_init(&ff.r, im, rad * 2 + 1);
+
+  i_circle_aa_low(im, x, y, rad, scanline_flush_fill, &ff);
+
+  i_render_done(&ff.r);
+}
+
+static void
+i_circle_aa_low(i_img *im, double x, double y, double rad, flush_render_t r,
+               void *ctx) {
   i_color temp;
   i_img_dim ly;
   dIMCTXim(im);
   i_img_dim first_row = floor(y) - ceil(rad);
   i_img_dim last_row = ceil(y) + ceil(rad);
   double r_sqr = rad * rad;
-  /*i_img_dim max_width = 2 * ceil(rad);
-  i_sample_t *coverage = NULL;
-  size_t coverage_size;*/
+  i_img_dim max_width = 2 * ceil(rad) + 1;
+  unsigned char *coverage = NULL;
+  size_t coverage_size;
   int sub;
 
-  im_log((aIMCTX, 1, "i_circle_aa(im %p, centre(" i_DFp "), rad %.2f, val %p)\n",
-         im, i_DFcp(x, y), rad, val));
+  im_log((aIMCTX, 1, "i_circle_aa_low(im %p, centre(" i_DFp "), rad %.2f, r %p, ctx %p)\n",
+         im, i_DFcp(x, y), rad, r, ctx));
 
   if (first_row < 0)
     first_row = 0;
@@ -461,9 +521,8 @@ i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
     return;
   }
 
-  /*  coverage_size = sizeof(i_sample_t) * max_width;
-      coverage = mymalloc(coverage_size);*/
+  coverage_size = max_width;
+  coverage = mymalloc(coverage_size);
 
   for(ly = first_row; ly < last_row; ly++) {
     frac min_frac_x[16];
@@ -514,12 +573,13 @@ i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
       i_img_dim right_solid = min_frac_right_x / 16;
       i_img_dim work_x;
       i_img_dim frac_work_x;
+      i_sample_t *cout = coverage;
 
       for (work_x = min_x, frac_work_x = min_x * 16;
           work_x <= max_x;
           ++work_x, frac_work_x += 16) {
        if (work_x <= left_solid || work_x >= right_solid) {
-         int coverage = 0;
+         int pix_coverage = 0;
          int ch;
          double ratio;
          i_img_dim frac_work_right = frac_work_x + 16;
@@ -533,26 +593,40 @@ i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
                pix_left = frac_work_x;
              if (pix_right > frac_work_right)
                pix_right = frac_work_right;
-             coverage += pix_right - pix_left;
+             pix_coverage += pix_right - pix_left;
            }
          }
 
-         assert(coverage <= 256);
-         ratio = coverage / 256.0;
-         i_gpix(im, work_x, ly, &temp);
-         for(ch=0;ch<im->channels; ch++)
-           temp.channel[ch] = (unsigned char)((float)val->channel[ch]*ratio + (float)temp.channel[ch]*(1.0-ratio));
-         i_ppix(im, work_x, ly, &temp);
+         assert(pix_coverage <= 256);
+         *cout++ = pix_coverage * 255 / 256;
        }
        else {
          /* full coverage */
-         i_ppix(im, work_x, ly, val);
+         *cout++ = 255;
        }
       }
+      r(im, min_x, ly, max_x - min_x + 1, coverage, ctx);
     }
   }
+
+  myfree(coverage);
+}
+
+static void
+scanline_flush_color(i_img *im, i_img_dim x, i_img_dim y, i_img_dim width, const unsigned char *cover, void *ctx) {
+  flush_color_t *fc = ctx;
+
+  i_render_color(&fc->r, x, y, width, cover, &fc->c);
 }
 
+static void
+scanline_flush_fill(i_img *im, i_img_dim x, i_img_dim y, i_img_dim width, const unsigned char *cover, void *ctx) {
+  flush_fill_t *ff = ctx;
+
+  i_render_fill(&ff->r, x, y, width, cover, ff->fill);
+}
+
+
 /*
 =item i_circle_out(im, x, y, r, col)
 
index c494044..0d42826 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -128,6 +128,7 @@ void i_arc_aa         (i_img *im, double x, double y, double rad, double d1, dou
 void i_arc_cfill(i_img *im,i_img_dim x,i_img_dim y,double rad,double d1,double d2,i_fill_t *fill);
 void i_arc_aa_cfill(i_img *im,double x,double y,double rad,double d1,double d2,i_fill_t *fill);
 void i_circle_aa   (i_img *im,double x, double y,double rad,const i_color *val);
+void i_circle_aa_fill(i_img *im,double x, double y,double rad,i_fill_t *fill);
 int i_circle_out   (i_img *im,i_img_dim x, i_img_dim y, i_img_dim rad,const i_color *val);
 int i_circle_out_aa   (i_img *im,i_img_dim x, i_img_dim y, i_img_dim rad,const i_color *val);
 void i_copyto      (i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty);