Code

patch 1598684: better trace quantization
authorbuliabyak <buliabyak@users.sourceforge.net>
Mon, 20 Nov 2006 05:26:29 +0000 (05:26 +0000)
committerbuliabyak <buliabyak@users.sourceforge.net>
Mon, 20 Nov 2006 05:26:29 +0000 (05:26 +0000)
src/trace/Makefile_insert
src/trace/filterset.cpp
src/trace/filterset.h
src/trace/pool.h [new file with mode: 0644]
src/trace/potrace/inkscape-potrace.cpp
src/trace/quantize.cpp [new file with mode: 0644]
src/trace/quantize.h [new file with mode: 0644]

index 2c8edcb1527ca8733705f7f0f13d3b3f5c2fbaa3..5ef59b3c3b9293fdff96bd2b2815d30062784934 100644 (file)
@@ -6,13 +6,16 @@ trace/clean:
        rm -f trace/libtrace.a $(trace_libtrace_a_OBJECTS)
 
 trace_libtrace_a_SOURCES =                 \
+       trace/pool.h                        \
        trace/trace.h                       \
        trace/trace.cpp                     \
        trace/imagemap-gdk.cpp              \
        trace/imagemap-gdk.h                \
        trace/imagemap.cpp                  \
        trace/imagemap.h                    \
-       trace/filterset.h                   \
+       trace/quantize.h                    \
+       trace/quantize.cpp                  \
+       trace/filterset.h                   \
        trace/filterset.cpp                 \
        trace/siox.h                        \
        trace/siox.cpp                      \
index 2dace61ce44d007108c4f6dce93b394c0da44bf0..e82ad4ed92c875e2a65b782c058a81ed061411a3 100644 (file)
@@ -3,8 +3,9 @@
  *
  * Authors:
  *   Bob Jamison <rjamison@titan.com>
+ *   Stéphane Gimenez <dev@gim.name>
  *
- * Copyright (C) 2004 Bob Jamison
+ * Copyright (C) 2004-2006 Authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
@@ -14,7 +15,7 @@
 
 #include "imagemap-gdk.h"
 #include "filterset.h"
-
+#include "quantize.h"
 
 /*#########################################################################
 ### G A U S S I A N  (smoothing)
@@ -405,500 +406,9 @@ gdkCanny(GdkPixbuf *img, double lowThreshold, double highThreshold)
     return newImg;
 }
 
-
-
-
 /*#########################################################################
 ### Q U A N T I Z A T I O N
 #########################################################################*/
-typedef struct OctreeNode_def OctreeNode;
-
-/**
- * The basic octree node
- */
-struct OctreeNode_def
-{
-    unsigned long r;
-    unsigned long g;
-    unsigned long b;
-    unsigned int  index;
-    unsigned long nrPixels;
-    unsigned int  nrChildren;
-    OctreeNode *parent;
-    OctreeNode *children[8];
-};
-
-
-/**
- * create an octree node, and initialize it
- */
-OctreeNode *octreeNodeCreate()
-{
-    OctreeNode *node = (OctreeNode *)malloc(sizeof(OctreeNode));
-    if (!node)
-        return NULL;
-    node->r             = 0;
-    node->g             = 0;
-    node->b             = 0;
-    node->index         = 0;
-    node->nrPixels      = 0;
-    node->nrChildren    = 0;
-    node->parent        = NULL;
-    for (int i=0 ; i<8 ; i++)
-        node->children[i] = NULL;
-    return node;
-}
-
-/**
- *  delete an octree node and its children
- */
-void octreeNodeDelete(OctreeNode *node)
-{
-    if (!node)
-        return;
-    for (int i=0 ; i<8 ; i++)
-        octreeNodeDelete(node->children[i]);
-    free(node);
-}
-
-
-/**
- *  delete the children of an octree node
- */
-void octreeNodeDeleteChildren(OctreeNode *node)
-{
-    if (!node)
-        return;
-    node->nrChildren = 0;
-    for (int i=0 ; i<8 ; i++)
-        {
-        octreeNodeDelete(node->children[i]);
-        node->children[i]=NULL;
-        }
-}
-
-
-
-
-/**
- *  insert an RGB value into an octree node according to its
- *  high-order rgb vector bits
- */
-int octreeNodeInsert(OctreeNode *root, RGB rgb, int bitsPerSample)
-{
-    OctreeNode *node = root;
-    int newColor     = FALSE;
-    int r            = rgb.r;
-    int g            = rgb.g;
-    int b            = rgb.b;
-
-    int shift = 7;
-    for (int bit=0 ; bit<bitsPerSample ; bit++)
-        {
-        /* update values of all nodes from the root to the leaf */
-        node->r += r;
-        node->g += g;
-        node->b += b;
-        node->nrPixels++;
-        int index = (((r >> shift) & 1) << 2) |
-                   (((g >> shift) & 1) << 1) |
-                    (((b >> shift) & 1)     ) ;
-
-        OctreeNode *child = node->children[index];
-        if (!child)
-            {
-            child                 = octreeNodeCreate();
-            node->children[index] = child;
-            child->parent         = node;
-            node->nrChildren++;
-           newColor              = TRUE;
-            }
-        node = child; /*next level*/
-        shift--;
-        }
-    return newColor;
-}
-
-
-
-
-
-/**
- *  find an exact match for an RGB value, at the given bits
- *  per sample.  if not found, return -1
- */
-int octreeNodeFind(OctreeNode *root, RGB rgb, int bitsPerSample)
-{
-    OctreeNode *node = root;
-    int r            = rgb.r;
-    int g            = rgb.g;
-    int b            = rgb.b;
-
-    int shift = 7;
-    for (int bit=0 ; bit<bitsPerSample ; bit++)
-        {
-        int index = (((r >> shift) & 1) << 2) |
-                   (((g >> shift) & 1) << 1) |
-                    (((b >> shift) & 1)     ) ;
-
-        OctreeNode *child = node->children[index];
-        if (!child)
-            return -1;
-        node = child; /*next level*/
-        shift--;
-        }
-    printf("error.  this should not happen\n");
-    return -1;
-}
-
-
-
-static void spaces(int nr)
-{
-    for (int i=0; i<nr ; i++)
-        printf(" ");
-}
-
-/**
- *  pretty-print an octree node and its children
- */
-void octreeNodePrint(OctreeNode *node, int indent)
-{
-    spaces(indent); printf("####Node###\n");
-    spaces(indent); printf("r :%lu\n", node->r);
-    spaces(indent); printf("g :%lu\n", node->g);
-    spaces(indent); printf("b :%lu\n", node->b);
-    spaces(indent); printf("i :%d\n", node->index);
-    for (unsigned int i=0; i<8; i++)
-        {
-        OctreeNode *child = node->children[i];
-        if (!child)
-            continue;
-        spaces(indent); printf("   child %d :", i);
-        octreeNodePrint(child, indent+4);
-        }
-}
-
-/**
- *  Count how many nodes in this (sub)tree
- */
-static int octreeNodeCount(OctreeNode *node)
-{
-    int count = 1;
-    for (unsigned int i=0; i<8; i++)
-        {
-        OctreeNode *child = node->children[i];
-        if (!child)
-            continue;
-        count += octreeNodeCount(child);
-        }
-    return count;
-}
-
-
-
-
-/**
- * Count all of the leaf nodes in the octree
- */
-static void octreeLeafArray(OctreeNode *node, OctreeNode **array, int arraySize, int *len)
-{
-    if (!node)
-        return;
-    if (node->nrChildren == 0 && *len < arraySize)
-        {
-        array[*len] = node;
-        *len = *len + 1;
-        }
-    for (int i=0 ; i<8 ; i++)
-        octreeLeafArray(node->children[i], array, arraySize, len);
-}
-
-
-
-/**
- *  Count all of the leaf nodes in the octree
- */
-static int octreeLeafCount(OctreeNode *node)
-{
-    if (!node)
-        return 0;
-    if (node->nrChildren == 0)
-        return 1;
-    int leaves = 0;
-    for (int i=0 ; i<8 ; i++)
-        leaves += octreeLeafCount(node->children[i]);
-    return leaves;
-}
-
-/**
- * Mark all of the leaf nodes in the octree with an index nr
- */
-static void octreeLeafIndex(OctreeNode *node, int *index)
-{
-    if (!node)
-        return;
-    if (node->nrChildren == 0)
-        {
-        node->index = *index;
-        *index = *index + 1;
-        return;
-        }
-    for (int i=0 ; i<8 ; i++)
-        octreeLeafIndex(node->children[i], index);
-}
-
-
-
-/**
- * Find a node that has children, and that also
- * has the lowest pixel count
- */
-static void octreefindLowestLeaf(OctreeNode *node, OctreeNode **lowestLeaf)
-{
-    if (!node)
-        return;
-    if (node->nrChildren == 0)
-        {
-        if (node->nrPixels < (*lowestLeaf)->nrPixels)
-            *lowestLeaf = node;
-        return;
-        }
-   
-    for (int i=0 ; i<8 ; i++)
-        octreefindLowestLeaf(node->children[i], lowestLeaf);
-}
-
-
-/**
- * reduce the leaves on an octree to a given number
- */
-int octreePrune(OctreeNode *root, int nrColors)
-{
-    int leafCount = octreeLeafCount(root);
-
-    while (leafCount > nrColors)
-        {
-        OctreeNode *lowestLeaf = root;    
-        octreefindLowestLeaf(root, &lowestLeaf);
-
-        if (!lowestLeaf)
-            break; //should never happen
-
-       if (lowestLeaf==root)
-            {
-            printf("found no leaves\n");
-            continue;
-            }
-
-        OctreeNode *parent = lowestLeaf->parent;
-        if (!parent)
-            continue;
-
-        for (int i=0 ; i<8 ; i++)
-            {
-            OctreeNode *child = parent->children[i];
-            if (child == lowestLeaf)
-                {
-                parent->nrChildren--;
-                octreeNodeDelete(child);
-                parent->children[i] = NULL;
-                break;
-                }
-            }
-        /*printf("leafCount:%d lowPixels:%lu\n",
-               leafCount, lowestLeaf->nrPixels);*/
-        leafCount = octreeLeafCount(root);
-        }
-    int index = 0;
-    octreeLeafIndex(root, &index);
-    
-    //printf("leafCount:%d\n", leafCount);
-    //octreeNodePrint(root, 0);
-
-    return leafCount;
-}
-
-
-
-/**
- *
- */
-OctreeNode *octreeBuild(RgbMap *rgbMap, int bitsPerSample, int nrColors)
-{
-    OctreeNode *root = octreeNodeCreate();
-    if (!root)
-        return NULL;
-    for (int y=0 ; y<rgbMap->height ; y++)
-        {
-        for (int x=0 ; x<rgbMap->width ; x++)
-            {
-            RGB rgb = rgbMap->getPixel(rgbMap, x, y);
-            octreeNodeInsert(root, rgb, bitsPerSample);
-            }
-        }
-
-    if (octreePrune(root, nrColors)<0)
-        {
-        octreeNodeDelete(root);
-        return NULL;
-        }
-
-    /* octreeNodePrint(root, 0); */
-
-    return root;
-}
-
-
-/* Compare two rgb's for brightness, for the qsort() below */
-static int compRGB(const void *a, const void *b)
-{
-    RGB *ra = (RGB *)a;
-    RGB *rb = (RGB *)b;
-    int ba = ra->r + ra->g + ra->b;
-    int bb = rb->r + rb->g + rb->b;
-    int comp = ba - bb;
-    return comp;
-}
-
-/**
- *
- */
-RGB *makeRGBPalette(OctreeNode *root, int nrColors)
-{
-
-    OctreeNode **palette = (OctreeNode **)malloc(nrColors * sizeof(OctreeNode *));
-    if (!palette)
-        {
-        return NULL;
-        }
-    int len = 0;
-    octreeLeafArray(root, palette, nrColors, &len);
-
-    RGB *rgbpal = (RGB *)malloc(len * sizeof(RGB));
-    if (!rgbpal)
-        {
-        free(palette);
-        return NULL;
-        }
-
-    for (int i=0; i<len ; i++)
-        {
-        OctreeNode *node = palette[i];
-        RGB rgb;
-        if (node->nrPixels == 0)
-            {
-            continue;
-            }
-        rgb.r = (unsigned char)( (node->r / node->nrPixels) & 0xff);
-        rgb.g = (unsigned char)( (node->g / node->nrPixels) & 0xff);
-        rgb.b = (unsigned char)( (node->b / node->nrPixels) & 0xff);
-        int index = node->index;
-        //printf("Pal: %d %d %d %d\n", rgb.r, rgb.g, rgb.b, index);
-        rgbpal[index]=rgb;
-        }
-
-    free(palette);
-    
-    /* sort by brightness, to avoid high contrasts */
-    qsort((void *)rgbpal, len, sizeof(RGB), compRGB);
-
-    return rgbpal;
-}
-
-
-/**
- *  Return the closest color in the palette to the request
- */
-RGB lookupQuantizedRGB(RGB *rgbpalette, int paletteSize, RGB candidate, int *closestIndex)
-{
-    /* slow method */
-    unsigned long closestMatch = 10000000;
-    RGB closestRGB = { 0 , 0, 0 };
-    *closestIndex = 0;
-    for (int i=0 ; i<paletteSize ; i++)
-        {
-        RGB entry = rgbpalette[i];
-        unsigned long dr    = candidate.r - entry.r;
-        unsigned long dg    = candidate.g - entry.g;
-        unsigned long db    = candidate.b - entry.b;
-        unsigned long match = dr * dr + dg * dg + db * db;
-        if (match < closestMatch)
-            {
-            *closestIndex = i;
-            closestMatch  = match;
-            closestRGB    = entry;
-            }
-        }
-
-    return closestRGB;
-}
-
-
-/**
- * Quantize an RGB image to a reduced number of colors.  bitsPerSample
- * is usually 3 - 5 out of 8 to conserve cpu and memory
- */
-IndexedMap *rgbMapQuantize(RgbMap *rgbMap, int bitsPerSample, int nrColors)
-{
-    if (!rgbMap)
-        return NULL;
-
-    OctreeNode *otree = octreeBuild(rgbMap, bitsPerSample, nrColors);
-    if (!otree)
-        {
-        return NULL;
-        }
-        
-    /*Make sure we don't request more colors than actually exist*/
-    int nodeCount = octreeLeafCount(otree);
-    if (nodeCount < nrColors)
-        nrColors = nodeCount;
-
-    RGB *rgbpal = makeRGBPalette(otree, nrColors);
-    if (!rgbpal)
-        {
-        octreeNodeDelete(otree);
-        return NULL;
-        }
-
-    /*We have our original and palette. Make the new one*/
-    IndexedMap *newMap = IndexedMapCreate(rgbMap->width, rgbMap->height);
-    if (!newMap)
-        {
-        free(rgbpal);
-        octreeNodeDelete(otree);
-        return NULL;
-        }
-     
-    /* fill in the color lookup table */
-    for (int i=0 ; i< nrColors ; i++)
-        {
-        newMap->clut[i] = rgbpal[i];
-        }
-    newMap->nrColors = nrColors;
-
-    for (int y=0 ; y<rgbMap->height ; y++)
-        {
-        for (int x=0 ; x<rgbMap->width ; x++)
-            {
-            RGB rgb = rgbMap->getPixel(rgbMap, x, y);
-            //int indexNr = octreeNodeFind(otree, rgb, bitsPerSample);
-            //printf("i:%d\n", indexNr);
-            //RGB quantRgb = rgbpal[indexNr];
-            int closestIndex;
-            RGB quantRgb = lookupQuantizedRGB(rgbpal, nrColors, rgb, &closestIndex);
-            newMap->setPixel(newMap, x, y, (unsigned int)closestIndex); 
-            }
-        }
-
-    free(rgbpal);
-    octreeNodeDelete(otree);
-
-    return newMap;
-}
-
-
 
 /**
  *  Experimental.  Work on this later
@@ -906,18 +416,16 @@ IndexedMap *rgbMapQuantize(RgbMap *rgbMap, int bitsPerSample, int nrColors)
 GrayMap *quantizeBand(RgbMap *rgbMap, int nrColors)
 {
 
-    int bitsPerSample = 4;
-
     RgbMap *gaussMap = rgbMapGaussian(rgbMap);
     //gaussMap->writePPM(gaussMap, "rgbgauss.ppm");
 
-    IndexedMap *qMap = rgbMapQuantize(gaussMap, bitsPerSample, nrColors);
+    IndexedMap *qMap = rgbMapQuantize(gaussMap, nrColors);
     //qMap->writePPM(qMap, "rgbquant.ppm");
     gaussMap->destroy(gaussMap);
 
     GrayMap *gm = GrayMapCreate(rgbMap->width, rgbMap->height);
 
-    /* RGB is quantized.  There should now be a small set of (R+G+B) */
+    // RGB is quantized.  There should now be a small set of (R+G+B)
     for (int y=0 ; y<qMap->height ; y++)
         {
         for (int x=0 ; x<qMap->width ; x++)
@@ -928,7 +436,7 @@ GrayMap *quantizeBand(RgbMap *rgbMap, int nrColors)
                 sum = 765;
             else
                 sum = 0;
-            /*printf("%d %d %d : %d\n", rgb.r, rgb.g, rgb.b, index);*/
+           // printf("%d %d %d : %d\n", rgb.r, rgb.g, rgb.b, index);
             gm->setPixel(gm, x, y, sum);
             }
         }
@@ -939,12 +447,6 @@ GrayMap *quantizeBand(RgbMap *rgbMap, int nrColors)
 }
 
 
-
-
-
-
-
-
 /*#########################################################################
 ### E N D    O F    F I L E
 #########################################################################*/
index 5e73847bdeba54986499ec7cb43de847bcce56f8..eeafc079f184284a303a510bab0c6e255b4610b7 100644 (file)
@@ -3,8 +3,9 @@
  *
  * Authors:
  *   Bob Jamison <rjamison@titan.com>
+ *   Stéphane Gimenez <dev@gim.name>
  *
- * Copyright (C) 2004 Bob Jamison
+ * Copyright (C) 2004-2006 Authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
-#ifndef TRUE
-#define TRUE  1
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-/*#########################################################################
-### C A N N Y    E D G E    D E T E C T I O N
-#########################################################################*/
-
-
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-
 /**
- *
+ *  Apply gaussian blur to an GrayMap
  */
-GrayMap *grayMapGaussian(GrayMap *me);
+GrayMap *grayMapGaussian(GrayMap *gmap);
 
 /**
- *
+ *  Apply gaussian bluf to an RgbMap
  */
-RgbMap *rgbMapGaussian(RgbMap *me);
+RgbMap *rgbMapGaussian(RgbMap *rgbmap);
 
 /**
  *
  */
-GrayMap *grayMapCanny(GrayMap *gm
+GrayMap *grayMapCanny(GrayMap *gmap,
              double lowThreshold, double highThreshold);
 
 /**
@@ -57,12 +43,6 @@ GrayMap *grayMapCanny(GrayMap *gm,
 GdkPixbuf *gdkCanny(GdkPixbuf *img,
             double lowThreshold, double highThreshold);
 
-/**
- * Quantize an RGB image to a reduced number of colors.  bitsPerSample
- * is usually 3 - 5 out of 8 to conserve cpu and memory
- */
-IndexedMap *rgbMapQuantize(RgbMap *rgbMap, int bitsPerSample, int nrColors);
-
 /**
  *
  */
diff --git a/src/trace/pool.h b/src/trace/pool.h
new file mode 100644 (file)
index 0000000..3f722f5
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Pool memory allocation
+ *
+ * Authors:
+ *   Stéphane Gimenez <dev@gim.name>
+ *
+ * Copyright (C) 2004-2006 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+// not thread safe (a pool cannot be shared by threads safely)
+
+/*
+-- principle:
+
+ - user operations on a pool of objects of type T are:
+   - T *draw() : obtain a unused slot to store an object T
+   - void drop(T *) : realease a slot
+
+-- implementation:
+
+ - a pool for objects T is:
+
+   * blocks[64] : an array of allocated blocks of memory:
+     |---0--> block with capacity 64
+     |---1--> block with capacity 64
+     |---2--> block with capacity 128
+     |---3--> block with capacity 128
+     |---4--> block with capacity 256
+     |---5--> block with capacity 256
+     |---6--> block with capacity 512
+     |---7--> not yet allocated
+     :
+     |---k--> not yet allocated (future capacity ~ 2^(6+k/2))
+     :
+     '--63--> not yet allocated
+   * cblock : the index of the next unallocated block (here 7).
+   * next : a pointer to an unused slot inside an allocated bloc
+
+ - the first bytes of an unallocated slot inside a bloc are used to store a
+   pointer to some other unallocated slot. (this way, we keep a list of all
+   unused slots starting at <next>)
+
+ - insertions and deletions in this list are done at the root <next>.
+   if <next> points to NULL (no slots are availlable) when a draw()
+   operation is performed a new block is allocated, and the unused slots
+   list is filled with the allocated slots.
+
+ - memory is freed only at pool's deletion.
+
+*/
+
+#include <stdlib.h>
+
+template <typename T>
+class pool {
+
+ public:
+
+  pool()
+    {
+      cblock = 0;
+      size = sizeof(T) > sizeof(void *) ? sizeof(T) : sizeof(void *);
+      next = NULL;
+    }
+
+  ~pool()
+    {
+      for (int k = 0; k < cblock; k++)
+       free(block[k]);
+    }
+
+  T *draw()
+  {
+    if (!next) addblock();
+    void *p = next;
+    next = *(void **)p;
+    return (T *) p;
+  }
+
+  void drop(T *p)
+  {
+    *(void **)p = next;
+    next = (void *) p;
+  }
+
+ private:
+
+  int size;
+  int cblock;
+  void *block[64]; //enough to store unlimited number of objects
+  void *next;
+
+  void addblock()
+    {
+      int i = cblock++;
+      int blocksize = 1 << (6 + (i/2));
+      //printf("pool allocating block: %d (size:%d)...", i, blocksize);//debug
+      block[i] = (void *)malloc(blocksize * size);
+      if (!block[i]) throw std::bad_alloc();
+      void *p = block[i];
+      for (int k = 0; k < blocksize - 1; k++)
+       {
+         *(void**)p = (void *)((int)p + size);
+         p = (void *)((int)p + size);
+       }
+      *(void **)p = next;
+      next = block[i];
+      //printf("done\n");//debug
+    }
+
+};
+
index d1aa357fc3089dc15526b176c6bf67f90f1aacd6..c27309eb9109ab6f73c7bff64d4a062c4ca4f34d 100644 (file)
@@ -20,6 +20,7 @@
 #include <gtkmm.h>
 
 #include "trace/filterset.h"
+#include "trace/quantize.h"
 #include "trace/imagemap-gdk.h"
 
 #include <inkscape.h>
@@ -284,39 +285,21 @@ filterIndexed(PotraceTracingEngine &engine, GdkPixbuf * pixbuf)
 
     IndexedMap *newGm = NULL;
 
-    /*### Color quant multiscan ###*/
-    if (engine.getTraceType() == TRACE_QUANT_COLOR)
+    RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
+    if (engine.getMultiScanSmooth())
         {
-        RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
-        if (engine.getMultiScanSmooth())
-            {
-            RgbMap *gaussMap = rgbMapGaussian(gm);
-            newGm = rgbMapQuantize(gaussMap,  (int)log2(engine.getMultiScanNrColors())+2, engine.getMultiScanNrColors());
-            gaussMap->destroy(gaussMap);
-            }
-        else
-            {
-            newGm = rgbMapQuantize(gm,  (int)log2(engine.getMultiScanNrColors())+2, engine.getMultiScanNrColors());
-            }
-        gm->destroy(gm);
-        }
-
-    /*### Quant multiscan ###*/
-    else if (engine.getTraceType() == TRACE_QUANT_MONO)
+       RgbMap *gaussMap = rgbMapGaussian(gm);
+       newGm = rgbMapQuantize(gaussMap, engine.getMultiScanNrColors());
+       gaussMap->destroy(gaussMap);
+       }
+    else
         {
-        RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
-        if (engine.getMultiScanSmooth())
-            {
-            RgbMap *gaussMap = rgbMapGaussian(gm);
-            newGm = rgbMapQuantize(gaussMap, (int)log2(engine.getMultiScanNrColors())+2, engine.getMultiScanNrColors());
-            gaussMap->destroy(gaussMap);
-            }
-        else
-            {
-           newGm = rgbMapQuantize(gm, (int)log2(engine.getMultiScanNrColors())+2, engine.getMultiScanNrColors());
-            }
-        gm->destroy(gm);
+       newGm = rgbMapQuantize(gm, engine.getMultiScanNrColors());
+       }
+    gm->destroy(gm);
 
+    if (engine.getTraceType() == TRACE_QUANT_MONO)
+        {
         //Turn to grays
         for (int i=0 ; i<newGm->nrColors ; i++)
             {
@@ -571,14 +554,9 @@ PotraceTracingEngine::traceQuant(GdkPixbuf * thePixbuf)
                 {
                 int indx = (int) iMap->getPixel(iMap, col, row);
                 if (indx == colorIndex)
-                    {
                     gm->setPixel(gm, col, row, GRAYMAP_BLACK); //black
-                    }
-                else
-                    {
-                    if (!multiScanStack)
-                        gm->setPixel(gm, col, row, GRAYMAP_WHITE);//white
-                    }
+                else if (!multiScanStack)
+                    gm->setPixel(gm, col, row, GRAYMAP_WHITE); //white
                 }
             }
 
diff --git a/src/trace/quantize.cpp b/src/trace/quantize.cpp
new file mode 100644 (file)
index 0000000..8acfe0c
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Quantization for Inkscape
+ *
+ * Authors:
+ *   Stéphane Gimenez <dev@gim.name>
+ *
+ * Copyright (C) 2006 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cassert>
+#include <cstdio>
+#include <stdlib.h>
+#include <new>
+
+#include "pool.h"
+#include "imagemap.h"
+
+typedef struct Ocnode_def Ocnode;
+
+/**
+ * an octree node datastructure
+ */
+struct Ocnode_def
+{
+    Ocnode *parent;           // parent node
+    Ocnode **ref;             // node's reference
+    Ocnode *child[8];         // children
+    int nchild;               // number of children
+    int width;                // width level of this node
+    RGB rgb;                  // rgb's prefix of that node
+    unsigned long weight;     // number of pixels this node accounts for
+    unsigned long rs, gs, bs; // sum of pixels colors this node accounts for
+    int nleaf;                // number of leaves under this node
+    unsigned long mi;         // minimum impact
+};
+
+/*
+-- algorithm principle:
+
+- inspired by the octree method, we associate a tree to a given color map
+
+- nodes in those trees have this shape:
+
+                                parent
+                                   |
+        color_prefix(stored in rgb):width
+     colors_sum(stored in rs,gs,bs)/weight
+         /               |               \
+     child1           child2           child3
+
+- (grayscale) trees associated to pixels with colors 87 = 0b1010111 and
+  69 = 0b1000101 are:
+
+           .                 .    <-- roots of the trees
+           |                 |
+    1010111:0  and    1000101:0   <-- color prefixes, written in binary form
+         87/1              69/1   <-- color sums, written in decimal form
+
+- the result of merging the two trees is:
+
+                   .
+                   |
+                 10:5       <----- longest common prefix and binary width
+                156/2       <---.  of the covered color range.
+            /            \      |
+    1000101:0      1010111:0    '- sum of colors and quantity of pixels
+         69/1           87/1       this node accounts for
+
+  one should consider three cases when two trees are to be merged:
+  - one tree range is included in the range of the other one, and the first
+    tree has to be inserted as a child (or merged with the corresponding
+    child) of the other.
+  - their ranges are the same, and their children have to be merged under
+    a single root.
+  - ranges have no intersection, and a fork node has to be created (like in
+    the given example).
+
+- a tree for an image is built dividing the image in 2 parts and merging
+  the trees obtained recursively for the two parts. a tree for a one pixel
+  part is a leaf like one of those which were given above.
+
+- last, this tree is reduced a specified number of leaves, deleting first
+  leaves with minimal impact i.e. [ weight * 2^(2*parentwidth) ] value :
+  a fair approximation of the impact a leaf removal would have on the final
+  result : it's the corresponding covered area times the square of the
+  introduced color distance.
+
+  deletion of a node A below a node with only two children is done as
+  follows :
+
+  - when the brother is a leaf, the brother is deleted as well, both nodes
+    are then represented by their father.
+
+     |               |
+     .       ==>     .
+    / \
+   A   .
+
+  - otherwise the deletion of A deletes also his father, which plays no
+    role anymore:
+
+     |                |
+     .       ==>       \
+    / \                 |
+   A   .                .
+      / \              / \
+
+  in that way, every leaf removal operation really decreases the remaining
+  total number of leaves by one.
+
+- very last, color indexes are attributed to leaves; associated colors are
+  averages, computed from weight and color components sums.
+
+-- improvements to the usual octree method:
+
+- since this algorithm shall often be used to perform quantization using a
+  very low (2-16) set of colors and not with a usual 256 value, we choose
+  more carefully which nodes are to be deleted.
+
+- depth of leaves is not fixed to an arbitrary number (which should be 8
+  when color components are in 0-255), so there is no need to go down to a
+  depth of 8 for each pixel (at full precision), unless it is really
+  required.
+
+- tree merging also fastens the overall tree building, and intermediate
+  processing could be done.
+
+- a huge optimization against the stupid removal algorithm (i.e. find a best
+  match over the whole tree, remove it and do it again) was implemented:
+  nodes are marked with the minimal impact of the removal of a leaf below
+  it. we proceed to the removal recursively. we stop when current removal
+  level is above the current node minimal, otherwise reached leaves are
+  removed, and every change over minimal impacts is propagated back to the
+  whole tree when the recursion ends.
+
+-- specific optimizations
+
+- pool allocation is used to allocate nodes (increased performance on large
+  images).
+
+*/
+
+inline RGB operator>>(RGB rgb, int s)
+{
+  RGB res;
+  res.r = rgb.r >> s; res.g = rgb.g >> s; res.b = rgb.b >> s;
+  return res;
+}
+inline bool operator==(RGB rgb1, RGB rgb2)
+{
+  return (rgb1.r == rgb2.r && rgb1.g == rgb2.g && rgb1.b == rgb2.b);
+}
+
+inline int childIndex(RGB rgb)
+{
+    return (((rgb.r)&1)<<2) | (((rgb.g)&1)<<1) | (((rgb.b)&1));
+}
+
+/**
+ * allocate a new node
+ */
+inline Ocnode *ocnodeNew(pool<Ocnode> *pool)
+{
+    Ocnode *node = pool->draw();
+    node->ref = NULL;
+    node->parent = NULL;
+    node->nchild = 0;
+    for (int i = 0; i < 8; i++) node->child[i] = NULL;
+    node->mi = 0;
+    return node;
+}
+
+inline void ocnodeFree(pool<Ocnode> *pool, Ocnode *node) {
+    pool->drop(node);
+}
+
+
+/**
+ * free a full octree
+ */
+static void octreeDelete(pool<Ocnode> *pool, Ocnode *node)
+{
+    if (!node) return;
+    for (int i = 0; i < 8; i++)
+        octreeDelete(pool, node->child[i]);
+    ocnodeFree(pool, node);
+}
+
+/**
+ *  pretty-print an octree, debugging purposes
+ */
+static void ocnodePrint(Ocnode *node, int indent)
+{
+    if (!node) return;
+    printf("width:%d weight:%lu rgb:%6x nleaf:%d mi:%lu\n",
+           node->width,
+           node->weight,
+           (unsigned int)(
+           ((node->rs / node->weight) << 16) +
+           ((node->gs / node->weight) << 8) +
+           (node->bs / node->weight)),
+           node->nleaf,
+           node->mi
+           );
+    for (int i = 0; i < 8; i++) if (node->child[i])
+        {
+        for (int k = 0; k < indent; k++) printf(" ");//indentation
+        printf("[%d:%p] ", i, node->child[i]);
+        ocnodePrint(node->child[i], indent+2);
+        }
+}
+void octreePrint(Ocnode *node)
+{
+    printf("<<octree>>\n");
+    if (node) printf("[r:%p] ", node); ocnodePrint(node, 2);
+}
+
+/**
+ * builds a single <rgb> color leaf at location <ref>
+ */
+void ocnodeLeaf(pool<Ocnode> *pool, Ocnode **ref, RGB rgb)
+{
+    assert(ref);
+    Ocnode *node = ocnodeNew(pool);
+    node->width = 0;
+    node->rgb = rgb;
+    node->rs = rgb.r; node->gs = rgb.g; node->bs = rgb.b;
+    node->weight = 1;
+    node->nleaf = 1;
+    node->mi = 0;
+    node->ref = ref;
+    *ref = node;
+}
+
+/**
+ *  merge nodes <node1> and <node2> at location <ref> with parent <parent>
+ */
+int octreeMerge(pool<Ocnode> *pool, Ocnode *parent, Ocnode **ref, Ocnode *node1, Ocnode *node2)
+{
+    assert(ref);
+    if (!node1 && !node2) return 0;
+    assert(node1 != node2);
+    if (parent && !*ref) parent->nchild++;
+    if (!node1)
+        {
+        *ref = node2; node2->ref = ref; node2->parent = parent;
+        return node2->nleaf;
+        }
+    if (!node2)
+        {
+        *ref = node1; node1->ref = ref; node1->parent = parent;
+        return node1->nleaf;
+        }
+    int dwitdth = node1->width - node2->width;
+    if (dwitdth > 0 && node1->rgb == node2->rgb >> dwitdth)
+        {
+        //place node2 below node1
+        { *ref = node1; node1->ref = ref; node1->parent = parent; }
+        int i = childIndex(node2->rgb >> (dwitdth - 1));
+        node1->rs += node2->rs; node1->gs += node2->gs; node1->bs += node2->bs;
+        node1->weight += node2->weight;
+       node1->mi = 0;
+        if (node1->child[i]) node1->nleaf -= node1->child[i]->nleaf;
+        node1->nleaf +=
+          octreeMerge(pool, node1, &node1->child[i], node1->child[i], node2);
+        return node1->nleaf;
+        }
+    else if (dwitdth < 0 && node2->rgb == node1->rgb >> (-dwitdth))
+        {
+        //place node1 below node2
+        { *ref = node2; node2->ref = ref; node2->parent = parent; }
+        int i = childIndex(node1->rgb >> (-dwitdth - 1));
+        node2->rs += node1->rs; node2->gs += node1->gs; node2->bs += node1->bs;
+        node2->weight += node1->weight;
+       node2->mi = 0;
+        if (node2->child[i]) node2->nleaf -= node2->child[i]->nleaf;
+        node2->nleaf +=
+          octreeMerge(pool, node2, &node2->child[i], node2->child[i], node1);
+        return node2->nleaf;
+        }
+    else
+        {
+        //nodes have either no intersection or the same root
+        Ocnode *newnode;
+        newnode = ocnodeNew(pool);
+        newnode->rs = node1->rs + node2->rs;
+        newnode->gs = node1->gs + node2->gs;
+        newnode->bs = node1->bs + node2->bs;
+        newnode->weight = node1->weight + node2->weight;
+        { *ref = newnode; newnode->ref = ref; newnode->parent = parent; }
+        if (dwitdth == 0 && node1->rgb == node2->rgb)
+            {
+            //merge the nodes in <newnode>
+            newnode->width = node1->width; // == node2->width
+            newnode->rgb = node1->rgb;     // == node2->rgb
+            newnode->nchild = 0;
+            newnode->nleaf = 0;
+            if (node1->nchild == 0 && node2->nchild == 0)
+                newnode->nleaf = 1;
+            else
+                for (int i = 0; i < 8; i++)
+                 if (node1->child[i] || node2->child[i])
+                    newnode->nleaf +=
+                     octreeMerge(pool, newnode, &newnode->child[i],
+                                 node1->child[i], node2->child[i]);
+            ocnodeFree(pool, node1); ocnodeFree(pool, node2);
+            return newnode->nleaf;
+            }
+        else
+            {
+            //use <newnode> as a fork node with children <node1> and <node2>
+            int newwidth =
+              node1->width > node2->width ? node1->width : node2->width;
+            RGB rgb1 = node1->rgb >> (newwidth - node1->width);
+            RGB rgb2 = node2->rgb >> (newwidth - node2->width);
+            //according to the previous tests <rgb1> != <rgb2> before the loop
+            while (!(rgb1 == rgb2))
+              { rgb1 = rgb1 >> 1; rgb2 = rgb2 >> 1; newwidth++; };
+            newnode->width = newwidth;
+            newnode->rgb = rgb1;  // == rgb2
+            newnode->nchild = 2;
+            newnode->nleaf = node1->nleaf + node2->nleaf;
+            int i1 = childIndex(node1->rgb >> (newwidth - node1->width - 1));
+            int i2 = childIndex(node2->rgb >> (newwidth - node2->width - 1));
+            node1->parent = newnode;
+            node1->ref = &newnode->child[i1];
+            newnode->child[i1] = node1;
+            node2->parent = newnode;
+            node2->ref = &newnode->child[i2];
+            newnode->child[i2] = node2;
+            return newnode->nleaf;
+            }
+        }
+}
+
+/**
+ * upatade mi value for leaves
+ */
+static inline void ocnodeMi(Ocnode *node)
+{
+    node->mi = node->parent ?
+       node->weight << (2 * node->parent->width) : 0;
+}
+
+/**
+ * remove leaves whose prune impact value is lower than <lvl>. at most
+ * <count> leaves are removed, and <count> is decreased on each removal.
+ * all parameters including minimal impact values are regenerated.
+ */
+static void ocnodeStrip(pool<Ocnode> *pool, Ocnode **ref, int *count, unsigned long lvl)
+{
+    Ocnode *node = *ref;
+    if (!count || !node) return;
+    assert(ref == node->ref);
+    if (node->nchild == 0) // leaf node
+        {
+        if (!node->mi) ocnodeMi(node); //mi generation may be required
+       if (node->mi > lvl) return; //leaf is above strip level
+        ocnodeFree(pool, node);
+        *ref = NULL;
+        (*count)--;
+        }
+    else
+        {
+       if (node->mi && node->mi > lvl) //node is above strip level
+            return;
+        node->nchild = 0;
+        node->nleaf = 0;
+        node->mi = 0;
+        Ocnode **lonelychild = NULL;
+        for (int i = 0; i < 8; i++) if (node->child[i])
+            {
+            ocnodeStrip(pool, &node->child[i], count, lvl);
+            if (node->child[i])
+                {
+                lonelychild = &node->child[i];
+                node->nchild++;
+                node->nleaf += node->child[i]->nleaf;
+                if (!node->mi || node->mi > node->child[i]->mi)
+                    node->mi = node->child[i]->mi;
+                }
+            }
+      // tree adjustments
+        if (node->nchild == 0)
+            {
+            (*count)++;
+            node->nleaf = 1;
+           ocnodeMi(node);
+            }
+        else if (node->nchild == 1)
+            {
+            if ((*lonelychild)->nchild == 0)
+                {
+                //remove the <lonelychild> leaf under a 1 child node
+                node->nchild = 0;
+                node->nleaf = 1;
+               ocnodeMi(node);
+                ocnodeFree(pool, *lonelychild);
+                *lonelychild = NULL;
+                }
+            else
+                {
+                //make a bridge to <lonelychild> over a 1 child node
+                (*lonelychild)->parent = node->parent;
+                (*lonelychild)->ref = ref;
+                ocnodeFree(pool, node);
+                *ref = *lonelychild;
+                }
+            }
+        }
+}
+
+/**
+ * reduce the leaves of an octree to a given number
+ */
+void octreePrune(pool<Ocnode> *pool, Ocnode **ref, int ncolor)
+{
+  assert(ref);
+  assert(ncolor > 0);
+  //printf("pruning down to %d colors:\n", ncolor);//debug
+  int n = (*ref)->nleaf - ncolor;
+  if (!*ref || n <= 0) return;
+  while (n > 0)
+      {
+      //printf("removals to go: %10d\t", n);//debug
+      //printf("current prune impact: %10lu\n", (*ref)->mi);//debug
+      //calling strip with global minimum impact of the tree
+      ocnodeStrip(pool, ref, &n, (*ref)->mi);
+      }
+}
+
+/**
+ * build an octree associated to the area of a color map <rgbmap>,
+ * included in the specified (x1,y1)--(x2,y2) rectangle.
+ */
+void octreeBuildArea(pool<Ocnode> *pool, RgbMap *rgbmap, Ocnode **ref,
+                     int x1, int y1, int x2, int y2, int ncolor)
+{
+    int dx = x2 - x1, dy = y2 - y1;
+    int xm = x1 + dx/2, ym = y1 + dy/2;
+    Ocnode *ref1 = NULL;
+    Ocnode *ref2 = NULL;
+    if (dx == 1 && dy == 1)
+        ocnodeLeaf(pool, ref, rgbmap->getPixel(rgbmap, x1, y1));
+    else if (dx > dy)
+        {
+       octreeBuildArea(pool, rgbmap, &ref1, x1, y1, xm, y2, ncolor);
+       octreeBuildArea(pool, rgbmap, &ref2, xm, y1, x2, y2, ncolor);
+       octreeMerge(pool, NULL, ref, ref1, ref2);
+       }
+    else
+        {
+       octreeBuildArea(pool, rgbmap, &ref1, x1, y1, x2, ym, ncolor);
+       octreeBuildArea(pool, rgbmap, &ref2, x1, ym, x2, y2, ncolor);
+       octreeMerge(pool, NULL, ref, ref1, ref2);
+       }
+
+    //octreePrune(ref, 2*ncolor);
+    //affects result quality for almost same performance :/
+}
+
+/**
+ * build an octree associated to the <rgbmap> color map,
+ * pruned to <ncolor> colors.
+ */
+Ocnode *octreeBuild(pool<Ocnode> *pool, RgbMap *rgbmap, int ncolor)
+{
+    //create the octree
+    Ocnode *node = NULL;
+    octreeBuildArea(pool,
+                    rgbmap, &node,
+                    0, 0, rgbmap->width, rgbmap->height, ncolor
+                    );
+
+    //prune the octree
+    octreePrune(pool, &node, ncolor);
+
+    //octreePrint(node);//debug
+
+    return node;
+}
+
+/**
+ * compute the color palette associated to an octree.
+ */
+static void octreeIndex(Ocnode *node, RGB *rgbpal, int *index)
+{
+    if (!node) return;
+    if (node->nchild == 0)
+        {
+        rgbpal[*index].r = node->rs / node->weight;
+        rgbpal[*index].g = node->gs / node->weight;
+        rgbpal[*index].b = node->bs / node->weight;
+        (*index)++;
+        }
+    else
+        for (int i = 0; i < 8; i++)
+            if (node->child[i])
+                octreeIndex(node->child[i], rgbpal, index);
+}
+
+/**
+ * compute the squared distance between two colors
+ */
+static int distRGB(RGB rgb1, RGB rgb2)
+{
+    return
+      (rgb1.r - rgb2.r) * (rgb1.r - rgb2.r)
+    + (rgb1.g - rgb2.g) * (rgb1.g - rgb2.g)
+    + (rgb1.b - rgb2.b) * (rgb1.b - rgb2.b);
+}
+
+/**
+ * find the index of closest color in a palette
+ */
+static int findRGB(RGB *rgbpal, int ncolor, RGB rgb)
+{
+    //assert(ncolor > 0);
+    //assert(rgbpal);
+    int index = -1, dist = 0;
+    for (int k = 0; k < ncolor; k++)
+        {
+        int d = distRGB(rgbpal[k], rgb);
+        if (index == -1 || d < dist) { dist = d; index = k; }
+        }
+    return index;
+}
+
+/**
+ * (qsort) compare two colors for brightness
+ */
+static int compRGB(const void *a, const void *b)
+{
+    RGB *ra = (RGB *)a, *rb = (RGB *)b;
+    return (ra->r + ra->g + ra->b) - (rb->r + rb->g + rb->b);
+}
+
+/**
+ * quantize an RGB image to a reduced number of colors.
+ */
+IndexedMap *rgbMapQuantize(RgbMap *rgbmap, int ncolor)
+{
+    assert(rgbmap);
+    assert(ncolor > 0);
+
+    pool<Ocnode> pool;
+
+    Ocnode *tree;
+    try {
+      tree = octreeBuild(&pool, rgbmap, ncolor);
+    }
+    catch (std::bad_alloc& ex) {
+      return NULL; //should do smthg else?
+    }
+
+    RGB *rgbpal = new RGB[ncolor];
+    int indexes = 0;
+    octreeIndex(tree, rgbpal, &indexes);
+
+    octreeDelete(&pool, tree);
+
+    // stacking with increasing contrasts
+    qsort((void *)rgbpal, indexes, sizeof(RGB), compRGB);
+
+    // make the new map
+    IndexedMap *newmap = IndexedMapCreate(rgbmap->width, rgbmap->height);
+    if (!newmap) { delete rgbpal; return NULL; }
+
+    // fill in the color lookup table
+    for (int i = 0; i < indexes; i++) newmap->clut[i] = rgbpal[i];
+    newmap->nrColors = indexes;
+
+    // fill in new map pixels
+    for (int y = 0; y < rgbmap->height; y++)
+        {
+        for (int x = 0; x < rgbmap->width; x++)
+            {
+            RGB rgb = rgbmap->getPixel(rgbmap, x, y);
+            int index = findRGB(rgbpal, ncolor, rgb);
+            newmap->setPixel(newmap, x, y, index);
+            }
+        }
+
+    delete rgbpal;
+    return newmap;
+}
diff --git a/src/trace/quantize.h b/src/trace/quantize.h
new file mode 100644 (file)
index 0000000..fa43408
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ *  Quantization for Inkscape
+ *
+ * Authors:
+ *   Stéphane Gimenez <dev@gim.name>
+ *
+ * Copyright (C) 2006 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __QUANTIZE_H__
+#define __QUANTIZE_H__
+
+#include "imagemap.h"
+
+/**
+ * Quantize an RGB image to a reduced number of colors.
+ */
+IndexedMap *rgbMapQuantize(RgbMap *rgbmap, int nrColors);
+
+#endif /* __QUANTIZE_H__ */