Sfoglia il codice sorgente

Add proper Unicode handling to img2twit. This includes an UTF-8 encoder/decoder

and a special bit stack that can handle arbitrary bases within its stream. The
image encoder and decoder are now separate code paths.

git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@3521 92316355-f0b4-4df1-b90c-862c8a59935f
master
sam 15 anni fa
parent
commit
9d2ab87f85
1 ha cambiato i file con 544 aggiunte e 142 eliminazioni
  1. +544
    -142
      examples/img2twit.cpp

+ 544
- 142
examples/img2twit.cpp Vedi File

@@ -15,15 +15,46 @@
* User-definable settings.
*/

/* Debug mode */
#define DEBUG 1

/* Encoding iterations: 1000 gives a fast answer, 10000 gives good quality */
#define MAX_ITERATIONS 10000

/* The maximum message length */
#define MAX_MSG_LEN 140

/* The number of characters at disposal */
//#define NUM_CHARACTERS 0x7fffffff // The sky's the limit
//#define NUM_CHARACTERS 1111998 // Full valid Unicode set
//#define NUM_CHARACTERS 100507 // Full graphic Unicode
#define NUM_CHARACTERS 32768 // Chinese characters
//#define NUM_CHARACTERS 127 // ASCII
/* The Unicode characters at disposal - XXX: must be _ordered_ */
static const uint32_t unichars[] =
{
/* Printable ASCII (except space) */
0x0021, 0x007f,

/* Stupid symbols and Dingbats shit */
//0x25a0, 0x2600, /* Geometric Shapes */
//0x2600, 0x269e, 0x26a0, 0x26bd, 0x26c0, 0x26c4, /* Misc. Symbols */
//0x2701, 0x2705, 0x2706, 0x270a, 0x270c, 0x2728, 0x2729, 0x274c,
// 0x274d, 0x274e, 0x274f, 0x2753, 0x2756, 0x2757, 0x2758, 0x275f,
// 0x2761, 0x2795, 0x2798, 0x27b0, 0x27b1, 0x27bf, /* Dingbats */

/* Chinese-looking stuff */
//0x2e80, 0x2e9a, 0x2e9b, 0x2ef4, /* CJK Radicals Supplement */
//0x2f00, 0x2fd6, /* Kangxi Radicals */
//0x3400, 0x4db6, /* CJK Unified Ideographs Extension A */
//0x4e00, 0x9fa6, /* CJK Unified Ideographs */

/* Korean - most people don't know the difference anyway */
0xac00, 0xd7a4, /* Hangul Syllables */

/* More Chinese */
//0xf900, 0xfa2e, 0xfa30, 0xfa6b, 0xfa70, 0xfada, /* CJK Compat. Idgphs. */

/* TODO: there's also the U+20000 and U+2f800 planes, but they're
* not supported by the Twitter Javascript filter (yet?). */

/* End of list marker - XXX: don't remove! */
0x0000, 0x0000
};

/* The maximum image size we want to support */
#define MAX_W 4000
@@ -53,8 +84,9 @@ static unsigned int RANGE_S = 1;
static float TOTAL_BITS;
static float HEADER_BITS;
static float DATA_BITS;
static float POINT_BITS;
static float CELL_BITS;

static int NUM_CHARACTERS;
static unsigned int TOTAL_CELLS;

#define RANGE_SY (RANGE_S*RANGE_Y)
@@ -77,6 +109,274 @@ static int npoints = 0;
/* Global triangulation */
static Delaunay_triangulation dt;

/*
* Unicode stuff handling
*/

/* Return the number of chars in the unichars table */
static int count_unichars(void)
{
int ret = 0;

for(int u = 0; unichars[u] != unichars[u + 1]; u += 2)
ret += unichars[u + 1] - unichars[u];

return ret;
}

/* Get the ith Unicode character in our list */
static uint32_t index2uni(uint32_t i)
{
for(int u = 0; unichars[u] != unichars[u + 1]; u += 2)
if(i < unichars[u + 1] - unichars[u])
return unichars[u] + i;
else
i -= unichars[u + 1] - unichars[u];

return 0; /* Should not happen! */
}

/* Convert a Unicode character to its position in the compact list */
static uint32_t uni2index(uint32_t x)
{
uint32_t ret = 0;

for(int u = 0; unichars[u] != unichars[u + 1]; u += 2)
if(x < unichars[u + 1])
return ret + x - unichars[u];
else
ret += unichars[u + 1] - unichars[u];

return ret; /* Should not happen! */
}

static uint8_t const utf8_trailing[256] =
{
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

static uint32_t const utf8_offsets[6] =
{
0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL
};

static uint32_t fread_utf8(FILE *f)
{
int ch, i = 0, todo = -1;
uint32_t ret = 0;

for(;;)
{
ch = fgetc(f);
if(!ch)
return 0;
if(todo == -1)
todo = utf8_trailing[ch];
ret += ((uint32_t)ch) << (6 * (todo - i));
if(todo == i++)
return ret - utf8_offsets[todo];
}
}

static void fwrite_utf8(FILE *f, uint32_t x)
{
static const uint8_t mark[7] =
{
0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
};

char buf[8];
char *parser = buf;
size_t bytes;

if(x < 0x80)
{
fprintf(f, "%c", x);
return;
}

bytes = (x < 0x800) ? 2 : (x < 0x10000) ? 3 : 4;
parser += bytes;
*parser = '\0';

switch(bytes)
{
case 4: *--parser = (x | 0x80) & 0xbf; x >>= 6;
case 3: *--parser = (x | 0x80) & 0xbf; x >>= 6;
case 2: *--parser = (x | 0x80) & 0xbf; x >>= 6;
}
*--parser = x | mark[bytes];

fprintf(f, "%s", buf);
}

/*
* Our nifty non-power-of-two bitstack handling
*/

class bitstack
{
public:
bitstack() { init(0); }

char const *tostring()
{
int pos = sprintf(str, "0x%x", digits[msb]);

for(int i = msb - 1; i >= 0; i--)
pos += sprintf(str + pos, "%08x", digits[i]);

return str;
}

void push(uint32_t val, uint32_t range)
{
if(!range)
return;

mul(range);
add(val % range);
}

uint32_t pop(uint32_t range)
{
if(!range)
return 0;

return div(range);
}

bool isempty()
{
for(int i = msb; i >= 0; i--)
if(digits[i])
return false;

return true;
}

private:
bitstack(uint32_t i) { init(i); }

void init(uint32_t i)
{
memset(digits, 0, sizeof(digits));
digits[0] = i;
msb = 0;
}

/* Could be done much faster, but we don't care! */
void add(uint32_t x) { add(bitstack(x)); }
void sub(uint32_t x) { sub(bitstack(x)); }

void add(bitstack const &_b)
{
/* Copy the operand in case we get added to ourselves */
bitstack b = _b;
uint64_t x = 0;

if(msb < b.msb)
msb = b.msb;

for(int i = 0; i <= msb; i++)
{
uint64_t tmp = (uint64_t)digits[i] + (uint64_t)b.digits[i] + x;
digits[i] = tmp;
if((uint64_t)digits[i] == tmp)
x = 0;
else
{
x = 1;
if(i == msb)
msb++;
}
}
}

void sub(bitstack const &_b)
{
/* Copy the operand in case we get substracted from ourselves */
bitstack b = _b;
uint64_t x = 0;

/* We cannot substract a larger number! */
if(msb < b.msb)
{
init(0);
return;
}

for(int i = 0; i <= msb; i++)
{
uint64_t tmp = (uint64_t)digits[i] - (uint64_t)b.digits[i] - x;
digits[i] = tmp;
if((uint64_t)digits[i] == tmp)
x = 0;
else
{
x = 1;
if(i == msb)
{
/* Error: carry into MSB! */
init(0);
return;
}
}
}

while(msb > 0 && digits[msb] == 0) msb--;
}

void mul(uint32_t x)
{
bitstack b = *this;
init(0);

while(x)
{
if(x & 1)
add(b);
x /= 2;
b.add(b);
}
}

uint32_t div(uint32_t x)
{
bitstack b = *this;

for(int i = msb; i >= 0; i--)
{
uint64_t tmp = b.digits[i] + (((uint64_t)b.digits[i + 1]) << 32);
uint32_t res = tmp / x;
uint32_t rem = tmp % x;
digits[i]= res;
b.digits[i + 1] = 0;
b.digits[i] = rem;
}

while(msb > 0 && digits[msb] == 0) msb--;

return b.digits[0];
}

int msb;
uint32_t digits[MAX_MSG_LEN + 1]; /* This is a safe max value */
char str[(MAX_MSG_LEN + 1) * 8 + 1];
};

/*
* Point handling
*/

static unsigned int det_rand(unsigned int mod)
{
static unsigned long next = 1;
@@ -156,11 +456,13 @@ static void add_point(float x, float y, float r, float g, float b, float s)
npoints++;
}

#if 0
static void add_random_point()
{
points[npoints] = det_rand(RANGE_SYXRGB);
npoints++;
}
#endif

#define NB_OPS 20

@@ -438,37 +740,68 @@ add_random_point();
int main(int argc, char *argv[])
{
int opstats[2 * NB_OPS];
bitstack b;
pipi_image_t *src, *tmp, *dst;
double error = 1.0;
int width, height, ret = 0;
bool decode = (argc >= 2 && !strcmp(argv[1], "-o"));

/* Compute bit allocation */
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
if(!((argc == 2 && !decode) || (argc == 3 && decode)))
{
fprintf(stderr, "img2twit: wrong argument count\n");
fprintf(stderr, "Usage: img2twit <image> Encode image and print result to stdout\n");
fprintf(stderr, " img2twit -o <image> Read data from stdin and decode image to file\n");
return EXIT_FAILURE;
}

pipi_set_gamma(1.0);

/* Precompute bit allocation */
NUM_CHARACTERS = count_unichars();
TOTAL_BITS = MAX_MSG_LEN * logf(NUM_CHARACTERS) / logf(2);
fprintf(stderr, "Available bits: %f\n", TOTAL_BITS);
fprintf(stderr, "Maximum image resolution: %ix%i\n", MAX_W, MAX_H);
HEADER_BITS = logf(MAX_W * MAX_H) / logf(2);
fprintf(stderr, "Header bits: %f\n", HEADER_BITS);
DATA_BITS = TOTAL_BITS - HEADER_BITS;
fprintf(stderr, "Bits available for data: %f\n", DATA_BITS);
#if POINTS_PER_CELL == 1
POINT_BITS = logf(RANGE_SYXRGB) / logf(2);
CELL_BITS = logf(RANGE_SYXRGB) / logf(2);
#else
float coord_bits = logf((RANGE_Y * RANGE_X) * (RANGE_Y * RANGE_X + 1) / 2);
float other_bits = logf(RANGE_R * RANGE_G * RANGE_B * RANGE_S);
POINT_BITS = (coord_bits + 2 * other_bits) / logf(2);
// TODO: implement the following shit
//float coord_bits = logf((RANGE_Y * RANGE_X) * (RANGE_Y * RANGE_X + 1) / 2);
//float other_bits = logf(RANGE_R * RANGE_G * RANGE_B * RANGE_S);
//CELL_BITS = (coord_bits + 2 * other_bits) / logf(2);
CELL_BITS = 2 * logf(RANGE_SYXRGB) / logf(2);
#endif
fprintf(stderr, "Cell bits: %f\n", POINT_BITS);
TOTAL_CELLS = (int)(DATA_BITS / POINT_BITS);
fprintf(stderr, "Available cells: %i\n", TOTAL_CELLS);
fprintf(stderr, "Wasted bits: %f\n", DATA_BITS - POINT_BITS * TOTAL_CELLS);
TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS);

/* Load image */
pipi_set_gamma(1.0);
src = pipi_load(argv[1]);
width = pipi_get_image_width(src);
height = pipi_get_image_height(src);
if(decode)
{
/* Decoding mode: read UTF-8 text from stdin, find each
* character's index in our character list, and push it to our
* wonderful custom bitstream. */
uint32_t data[MAX_MSG_LEN];
for(int i = 0; i < MAX_MSG_LEN; i++)
data[i] = uni2index(fread_utf8(stdin));
for(int i = MAX_MSG_LEN; i--; )
b.push(data[i], NUM_CHARACTERS);

/* Read width and height from bitstream */
src = NULL;
width = b.pop(MAX_W);
height = b.pop(MAX_H);
}
else
{
/* Argument given: open image for encoding */
src = pipi_load(argv[1]);

if(!src)
{
fprintf(stderr, "Error loading %s\n", argv[1]);
return EXIT_FAILURE;
}

width = pipi_get_image_width(src);
height = pipi_get_image_height(src);
}

/* Compute best w/h ratio */
dw = 1; dh = TOTAL_CELLS;
@@ -488,145 +821,214 @@ int main(int argc, char *argv[])
}
while((dh + 1) * dw <= TOTAL_CELLS) dh++;
while(dw * (dh + 1) <= TOTAL_CELLS) dw++;

/* Print debug information */
#if DEBUG
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
fprintf(stderr, "Available bits: %f\n", TOTAL_BITS);
fprintf(stderr, "Maximum image resolution: %ix%i\n", MAX_W, MAX_H);
fprintf(stderr, "Image resolution: %ix%i\n", width, height);
fprintf(stderr, "Header bits: %f\n", HEADER_BITS);
fprintf(stderr, "Bits available for data: %f\n", DATA_BITS);
fprintf(stderr, "Cell bits: %f\n", CELL_BITS);
fprintf(stderr, "Available cells: %i\n", TOTAL_CELLS);
fprintf(stderr, "Wasted bits: %f\n", DATA_BITS - CELL_BITS * TOTAL_CELLS);

fprintf(stderr, "Chosen image ratio: %i:%i (wasting %i point cells)\n",
dw, dh, TOTAL_CELLS - dw * dh);
fprintf(stderr, "Total wasted bits: %f\n",
DATA_BITS - POINT_BITS * dw * dh);

/* Resize and filter image to better state */
tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
pipi_free(src);
src = pipi_median_ext(tmp, 1, 1);
pipi_free(tmp);

/* Analyse image */
analyse(src);

/* Render what we just computed */
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
error = pipi_measure_rmsd(src, tmp);

fprintf(stderr, "Distance: %2.10g\n", error);
DATA_BITS - CELL_BITS * dw * dh);
#endif

memset(opstats, 0, sizeof(opstats));
for(int iter = 0, stuck = 0, failures = 0, success = 0;
/*stuck < 5 && */iter < 10000;
iter++)
if(src)
{
if(failures > 500)
/* Resize and filter image to better state */
tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
pipi_free(src);
src = pipi_median_ext(tmp, 1, 1);
pipi_free(tmp);

/* Analyse image */
analyse(src);

/* Render what we just computed */
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
error = pipi_measure_rmsd(src, tmp);

#if DEBUG
fprintf(stderr, "Distance: %2.10g\n", error);
#endif

memset(opstats, 0, sizeof(opstats));
for(int iter = 0, stuck = 0, failures = 0, success = 0;
iter < MAX_ITERATIONS /* && stuck < 5 && */;
iter++)
{
stuck++;
failures = 0;
}
if(failures > 500)
{
stuck++;
failures = 0;
}

pipi_image_t *scrap = pipi_copy(tmp);
#if !DEBUG
if(!(iter % 16))
fprintf(stderr, "\rEncoding... %i%%",
iter * 100 / MAX_ITERATIONS);
#endif

pipi_image_t *scrap = pipi_copy(tmp);

/* Choose a point at random */
int pt = det_rand(npoints);
uint32_t oldval = points[pt];

/* Compute the affected image zone */
float fx, fy, fr, fg, fb, fs;
get_point(pt, &fx, &fy, &fr, &fg, &fb, &fs);
int zonex = (int)fx / RANGE_X - 1;
int zoney = (int)fy / RANGE_Y - 1;
int zonew = 3;
int zoneh = 3;
if(zonex < 0) { zonex = 0; zonew--; }
if(zoney < 0) { zoney = 0; zoneh--; }
if(zonex + zonew >= (int)dw) { zonew--; }
if(zoney + zoneh >= (int)dh) { zoneh--; }

/* Choose random operations and measure their effect */
uint8_t op1 = rand_op();
//uint8_t op2 = rand_op();

uint32_t candidates[3];
double besterr = error + 1.0;
int bestop = -1;
candidates[0] = apply_op(op1, oldval);
//candidates[1] = apply_op(op2, oldval);
//candidates[2] = apply_op(op1, apply_op(op2, oldval));

for(int i = 0; i < 1; i++)
//for(int i = 0; i < 3; i++)
{
if(oldval == candidates[i])
continue;

/* Choose a point at random */
int pt = det_rand(npoints);
uint32_t oldval = points[pt];
points[pt] = candidates[i];

/* Compute the affected image zone */
float fx, fy, fr, fg, fb, fs;
get_point(pt, &fx, &fy, &fr, &fg, &fb, &fs);
int zonex = (int)fx / RANGE_X - 1;
int zoney = (int)fy / RANGE_Y - 1;
int zonew = 3;
int zoneh = 3;
if(zonex < 0) { zonex = 0; zonew--; }
if(zoney < 0) { zoney = 0; zoneh--; }
if(zonex + zonew >= (int)dw) { zonew--; }
if(zoney + zoneh >= (int)dh) { zoneh--; }

/* Choose random operations and measure their effect */
uint8_t op1 = rand_op();
//uint8_t op2 = rand_op();

uint32_t candidates[3];
double besterr = error + 1.0;
int bestop = -1;
candidates[0] = apply_op(op1, oldval);
//candidates[1] = apply_op(op2, oldval);
//candidates[2] = apply_op(op1, apply_op(op2, oldval));

for(int i = 0; i < 1; i++)
//for(int i = 0; i < 3; i++)
{
if(oldval == candidates[i])
continue;
render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
zonew * RANGE_X, zoneh * RANGE_Y);

points[pt] = candidates[i];
double newerr = pipi_measure_rmsd(src, scrap);
if(newerr < besterr)
{
besterr = newerr;
bestop = i;
}
}

render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
zonew * RANGE_X, zoneh * RANGE_Y);
opstats[op1 * 2]++;
//opstats[op2 * 2]++;

double newerr = pipi_measure_rmsd(src, scrap);
if(newerr < besterr)
if(besterr < error)
{
besterr = newerr;
bestop = i;
points[pt] = candidates[bestop];
/* Redraw image if the last check wasn't the best one */
if(bestop != 2)
render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
zonew * RANGE_X, zoneh * RANGE_Y);

pipi_free(tmp);
tmp = scrap;
#if DEBUG
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n", iter,
(int)((error - besterr) * 100000000), error, op1, pt);
#endif
error = besterr;
opstats[op1 * 2 + 1]++;
//opstats[op2 * 2 + 1]++;
failures = 0;
success++;

/* Save image! */
//char buf[128];
//sprintf(buf, "twit%08i.bmp", success);
//if((success % 10) == 0)
// pipi_save(tmp, buf);
}
else
{
pipi_free(scrap);
points[pt] = oldval;
failures++;
}
}

opstats[op1 * 2]++;
//opstats[op2 * 2]++;

if(besterr < error)
{
points[pt] = candidates[bestop];
/* Redraw image if the last check wasn't the best one */
if(bestop != 2)
render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
zonew * RANGE_X, zoneh * RANGE_Y);
fprintf(stderr, "\r \r");

pipi_free(tmp);
tmp = scrap;
//fprintf(stderr, "%08i %2.010g %2.010g after op%i(%i)\n",
// iter, besterr - error, error, op1, pt);
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n", iter,
(int)((error - besterr) * 100000000), error, op1, pt);
error = besterr;
opstats[op1 * 2 + 1]++;
//opstats[op2 * 2 + 1]++;
failures = 0;
success++;

/* Save image! */
//char buf[128];
//sprintf(buf, "twit%08i.bmp", success);
//if((success % 10) == 0)
// pipi_save(tmp, buf);
}
else
#if DEBUG
for(int j = 0; j < 2; j++)
{
pipi_free(scrap);
points[pt] = oldval;
failures++;
fprintf(stderr, "operation: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", i);
fprintf(stderr, "\nattempts: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2]);
fprintf(stderr, "\nsuccesses: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2 + 1]);
fprintf(stderr, "\n");
}
}

for(int j = 0; j < 2; j++)
{
fprintf(stderr, "operation: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", i);
fprintf(stderr, "\nattempts: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2]);
fprintf(stderr, "\nsuccesses: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2 + 1]);
fprintf(stderr, "\n");
fprintf(stderr, "Distance: %2.10g\n", error);
#endif

#if 0
dst = pipi_resize(tmp, width, height);
pipi_free(tmp);

/* Save image and bail out */
pipi_save(dst, "lol.bmp");
pipi_free(dst);
#endif

/* Push our points to the bitstream */
for(int i = 0; i < npoints; i++)
b.push(points[i], RANGE_SYXRGB);
b.push(height, MAX_H);
b.push(width, MAX_W);

/* Pop Unicode characters from the bitstream and print them */
for(int i = 0; i < MAX_MSG_LEN; i++)
fwrite_utf8(stdout, index2uni(b.pop(NUM_CHARACTERS)));
fprintf(stdout, "\n");
}
else
{
/* Pop points from the bitstream */
for(int i = dw * dh; i--; )
{
#if POINTS_PER_CELL == 2
points[i * 2 + 1] = b.pop(RANGE_SYXRGB);
points[i * 2] = b.pop(RANGE_SYXRGB);
#else
points[i] = b.pop(RANGE_SYXRGB);
#endif
}
npoints = dw * dh * POINTS_PER_CELL;

fprintf(stderr, "Distance: %2.10g\n", error);
/* Render these points to a new image */
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);

dst = pipi_resize(tmp, width, height);
pipi_free(tmp);
/* TODO: render directly to the final image; scaling sucks */
dst = pipi_resize(tmp, width, height);
pipi_free(tmp);

/* Save image and bail out */
pipi_save(dst, "lol.bmp");
pipi_free(dst);
/* Save image and bail out */
pipi_save(dst, argv[2]);
pipi_free(dst);
}

return ret;
}


Caricamento…
Annulla
Salva