Gabriel Vasseur's patch, corrected just enough for it to compile.
authorTony Cook <tony@develop=help.com>
Fri, 24 Aug 2007 07:27:18 +0000 (07:27 +0000)
committerTony Cook <tony@develop=help.com>
Fri, 24 Aug 2007 07:27:18 +0000 (07:27 +0000)
This needs some cleanup:

- integrate the extra tests supplied
- test the method she didn't
- foolproofing
- documentation

https://rt.cpan.org/Ticket/Display.html?id=28142

Imager.pm
Imager.xs
datatypes.c
image.c

index 28a72bf..065b29b 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -3269,6 +3269,33 @@ sub getcolorcount {
   return ($rc==-1? undef : $rc);
 }
 
+# Returns a reference to a hash. The keys are colour named (packed) and the
+# values are the number of pixels in this colour.
+sub getcolorusagehash {
+    my $self = shift;
+    my $channels= $self->getchannels;
+    # We don't want to look at the alpha channel, because some gifs using it
+    # doesn't define it for every colour (but only for some)
+    $channels -= 1 if $channels == 2 or $channels == 4;
+    my %colour_use;
+    my $height = $self->getheight;
+    for my $y (0 .. $height - 1) {
+        my $colours = $self->getsamples(y => $y, channels => [ 0 .. $channels - 1 ]);
+        while (length $colours) {
+            $colour_use{ substr($colours, 0, $channels, '') }++;
+        }
+    }
+    return \%colour_use;
+}
+
+# This will return a ordered array of the colour usage. Kind of the sorted
+# version of the values of the hash returned by getcolorusagehash.
+# You might want to add safety checks and change the names, etc...
+sub getcolorusage {
+  my $self=shift;
+  return get_anonymous_colour_usage ($self);
+}
+
 # draw string to an image
 
 sub string {
index c44841c..0ccba04 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -2972,6 +2972,22 @@ i_count_colors(im,maxc)
     Imager::ImgRaw     im
                int     maxc
 
+void
+get_anonymous_colour_usage(Imager::ImgRaw im)
+    PPCODE:
+        int i;
+        unsigned int ** col_usage = (unsigned int **) mymalloc(sizeof(unsigned int *));
+        unsigned int * p;
+        int col_cnt = get_anonymous_color_histo(im, col_usage);
+        EXTEND(SP, col_cnt);
+        p = *col_usage;
+        for (i = 0; i < col_cnt; )  {
+            PUSHs(sv_2mortal(newSViv( p[i++])));
+        }
+        myfree(p);
+        myfree(col_usage);
+        XSRETURN(col_cnt);
+
 
 Imager::ImgRaw
 i_transform(im,opx,opy,parm)
index 9c04b18..dfbf6a6 100644 (file)
@@ -216,21 +216,22 @@ int
 octt_add(struct octt *ct,unsigned char r,unsigned char g,unsigned char b) {
   struct octt *c;
   int i,cm;
-  int ci,idx[8];
+  int ci;
   int rc;
   rc=0;
   c=ct;
   /*  printf("[r,g,b]=[%d,%d,%d]\n",r,g,b); */
-  ct->cnt++;
   for(i=7;i>-1;i--) {
     cm=1<<i;
     ci=((!!(r&cm))<<2)+((!!(g&cm))<<1)+!!(b&cm); 
     /* printf("idx[%d]=%d\n",i,ci); */
-    if (c->t[ci] == NULL) { c->t[ci]=octt_new(); rc=1; }
+    if (c->t[ci] == NULL) { 
+      c->t[ci]=octt_new(); 
+      rc=1; 
+    }
     c=c->t[ci];
-    c->cnt++;
-    idx[i]=ci;
   }
+  c->cnt++;  /* New. The only thing really needed (I think) */
   return rc;
 }
 
@@ -267,3 +268,22 @@ octt_count(struct octt *ct,int *tot,int max,int *overflow) {
   if (!c) (*tot)++;
   if ( (*tot) > (*overflow) ) *overflow=0;
 }
+
+/* This whole function is new */
+/* walk through the tree and for each colour, store its seen count in the
+   space pointed by *col_usage_it_adr */
+void
+octt_histo(struct octt *ct, unsigned int **col_usage_it_adr) {
+    int i,c;
+    c = 0;
+    for(i = 0; i < 8; i++) 
+        if (ct->t[i] != NULL) { 
+            octt_histo(ct->t[i], col_usage_it_adr);
+            c++;
+        }
+    if (!c) {
+        *(*col_usage_it_adr)++ = ct->cnt;
+    }
+}
+
+
diff --git a/image.c b/image.c
index 4f21c16..dfef965 100644 (file)
--- a/image.c
+++ b/image.c
@@ -1254,30 +1254,134 @@ to indicate that it was more than max colors
 
 =cut
 */
+/* This function has been changed and is now faster. It's using
+ * i_gsamp instead of i_gpix */
 int
 i_count_colors(i_img *im,int maxc) {
   struct octt *ct;
   int x,y;
-  int xsize,ysize;
-  i_color val;
   int colorcnt;
-
-  mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n"));
-
-  xsize=im->xsize; 
-  ysize=im->ysize;
-  ct=octt_new();
-  colorcnt=0;
-  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
-    i_gpix(im,x,y,&val);
-    colorcnt+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
-    if (colorcnt > maxc) { octt_delete(ct); return -1; }
+  int channels[3];
+  int *samp_chans;
+  i_sample_t * samp;
+
+  int xsize = im->xsize; 
+  int ysize = im->ysize;
+  if (im->channels >= 3) {
+    samp_chans = NULL;
+  }
+  else {
+    channels[0] = channels[1] = channels[2] = 0;
+    samp_chans = channels;
   }
+  int samp_cnt = 3 * xsize;
+  ct = octt_new();
+
+  samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t));
+
+  colorcnt = 0;
+  for(y = 0; y < ysize; ) {
+      i_gsamp(im, 0, xsize, y++, samp, samp_chans, 3);
+      for(x = 0; x < samp_cnt; ) {
+          colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
+          x += 3;
+          if (colorcnt > maxc) { 
+              octt_delete(ct); 
+              return -1; 
+          }
+      }
+  }
+  myfree(samp);
   octt_delete(ct);
   return colorcnt;
 }
 
+/* sorts the array ra[0..n-1] into increasing order using heapsort algorithm 
+ * (adapted from the Numerical Recipes)
+ */
+/* Needed by get_anonymous_color_histo */
+void hpsort(unsigned int n, int *ra) {
+    unsigned int i,
+                 ir,
+                 j,
+                 l, 
+                 rra;
+
+    if (n < 2) return;
+    l = n >> 1;
+    ir = n - 1;
+    for(;;) {
+        if (l > 0) {
+            rra = ra[--l];
+        }
+        else {
+            rra = ra[ir];
+            ra[ir] = ra[0];
+            if (--ir == 0) {
+                ra[0] = rra;
+                break;
+            }
+        }
+        i = l;
+        j = 2 * l + 1;
+        while (j <= ir) {
+            if (j < ir && ra[j] < ra[j+1]) j++;
+            if (rra < ra[j]) {
+                ra[i] = ra[j];
+                i = j;
+                j++; j <<= 1; j--;
+            }
+            else break;
+        }
+        ra[i] = rra;
+    }
+}
+
+/* This function constructs an ordered list which represents how much the
+ * different colors are used. So for instance (100, 100, 500) means that one
+ * color is used for 500 pixels, another for 100 pixels and another for 100
+ * pixels. It's tuned for performance. You might not like the way I've hardcoded
+ * the maxc ;-) and you might want to change the name... */
+/* Uses octt_histo */
+int
+get_anonymous_color_histo(i_img *im, unsigned int **col_usage) {
+    struct octt *ct;
+    int x,y,i;
+    int colorcnt;
+    int maxc = 10000000;
+    unsigned int *col_usage_it;
+    i_sample_t * samp;
+
+    int xsize = im->xsize; 
+    int ysize = im->ysize;
+    int samp_cnt = 3 * xsize;
+    ct = octt_new();
+
+    samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t));
+
+    colorcnt = 0;
+    for(y = 0; y < ysize; ) {
+        i_gsamp(im, 0, xsize, y++, samp, NULL, 3);
+        for(x = 0; x < samp_cnt; ) {
+            colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
+            x += 3;
+            if (colorcnt > maxc) { 
+                octt_delete(ct); 
+                return -1; 
+            }
+        }
+    }
+    myfree(samp);
+    /* Now that we know the number of colours... */
+    col_usage_it = *col_usage = (unsigned int *) mymalloc(colorcnt * 2 * sizeof(unsigned int));
+    octt_histo(ct, &col_usage_it);
+    hpsort(colorcnt, *col_usage);
+    octt_delete(ct);
+    return colorcnt;
+}
+
+
+
 /*
 =back