summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 2cdb4e5)
raw | patch | inline | side by side (parent: 2cdb4e5)
author | buliabyak <buliabyak@users.sourceforge.net> | |
Mon, 20 Nov 2006 05:26:29 +0000 (05:26 +0000) | ||
committer | buliabyak <buliabyak@users.sourceforge.net> | |
Mon, 20 Nov 2006 05:26:29 +0000 (05:26 +0000) |
src/trace/Makefile_insert | patch | blob | history | |
src/trace/filterset.cpp | patch | blob | history | |
src/trace/filterset.h | patch | blob | history | |
src/trace/pool.h | [new file with mode: 0644] | patch | blob |
src/trace/potrace/inkscape-potrace.cpp | patch | blob | history | |
src/trace/quantize.cpp | [new file with mode: 0644] | patch | blob |
src/trace/quantize.h | [new file with mode: 0644] | patch | blob |
index 2c8edcb1527ca8733705f7f0f13d3b3f5c2fbaa3..5ef59b3c3b9293fdff96bd2b2815d30062784934 100644 (file)
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)
--- a/src/trace/filterset.cpp
+++ b/src/trace/filterset.cpp
*
* 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 "imagemap-gdk.h"
#include "filterset.h"
-
+#include "quantize.h"
/*#########################################################################
### G A U S S I A N (smoothing)
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
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++)
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);
}
}
}
-
-
-
-
-
-
/*#########################################################################
### E N D O F F I L E
#########################################################################*/
diff --git a/src/trace/filterset.h b/src/trace/filterset.h
index 5e73847bdeba54986499ec7cb43de847bcce56f8..eeafc079f184284a303a510bab0c6e255b4610b7 100644 (file)
--- a/src/trace/filterset.h
+++ b/src/trace/filterset.h
*
* 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);
/**
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
--- /dev/null
+++ b/src/trace/pool.h
@@ -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)
#include <gtkmm.h>
#include "trace/filterset.h"
+#include "trace/quantize.h"
#include "trace/imagemap-gdk.h"
#include <inkscape.h>
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++)
{
{
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
--- /dev/null
+++ b/src/trace/quantize.cpp
@@ -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
--- /dev/null
+++ b/src/trace/quantize.h
@@ -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__ */