#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <helianthus.h>
#define COUNT 64
#define TEXCOUNT 512
#define FRAMECOUNT 512
#define W 512
#define H 512
#define STEP 0.05
#define EDGE 0
//#define SAVE "data/output/mosaic/frame%03d.png"
typedef struct {
int w, h;
unsigned char *pixels;
} Tex;
typedef struct {
Tex *tex0;
Tex *tex1;
double alpha;
} Frame;
typedef struct {
int x, y;
int l, t, w, h;
unsigned int color;
Frame frames[FRAMECOUNT];
} Point;
typedef struct {
double k[3];
Point *point;
double weight;
double alpha;
double edge;
Point *point2;
double weight2;
} Pixel;
Point points[COUNT];
Pixel pixels[H][W];
Tex textures[TEXCOUNT];
int texturesCnt;
unsigned char image[H][W][4];
Animation anim;
int wrapX(int x)
{ return (x%W + W)%W; }
int wrapY(int y)
{ return (y%H + H)%H; }
Pixel* getPixel(int x, int y)
{ return &pixels[wrapY(y)][wrapX(x)]; }
int updatePixel(Pixel *p, int x, int y, int mode) {
Pixel *pp = getPixel(x, y);
double w = p->weight + p->k[mode] + pp->k[mode];
if (!pp->point || pp->weight > w) {
pp->point2 = pp->point = p->point;
pp->weight2 = pp->weight = w;
return 1;
}
return 0;
}
void updatePixel2(Pixel *p, int x, int y, int mode) {
Pixel *pp = getPixel(x, y);
if (p->point == pp->point) return;
double w = pp->weight + pp->k[mode] + p->k[mode];
if (p->point2 == p->point || p->weight2 > w) {
p->point2 = pp->point;
p->weight2 = w;
}
}
void generateFrames(Frame *frames) {
int start;
int switches = 1 + rand()%10;
for(int i = 0; i < switches; ++i) {
start = rand()%FRAMECOUNT;
frames[start].tex1 = &textures[rand()%texturesCnt];
}
double alpha = 1;
Tex *t0 = frames[start].tex1, *t1 = t0;
for(int i = start; i < FRAMECOUNT*10; ++i) {
Frame *f = &frames[i%FRAMECOUNT];
if (alpha < 1) {
alpha += STEP;
if (alpha >= 1) {
alpha = 1;
t0 = t1;
}
} else
if (f->tex1 && f->tex1 != t1) {
t1 = f->tex1;
alpha = 0;
}
f->tex0 = t0;
f->tex1 = t1;
f->alpha = alpha;
}
//if (frames == points->frames)
// for(int i = 0; i < FRAMECOUNT; ++i)
// printf("%3d %3d %3d %f\n", i, frames[i].tex0 - textures, frames[i].tex1 - textures, frames[i].alpha);
}
Animation generateAnim(int frameId, const char *filename) {
// update image
Pixel *p = pixels[0];
unsigned char black[] = { 0, 0, 0, 255 };
//unsigned char white[] = { 255, 255, 255, 255 };
unsigned char *c = image[0][0];
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x, ++p) {
Frame *f0 = &p->point->frames[frameId];
Frame *f1 = &p->point2->frames[frameId];
Tex *t0 = f0->tex0;
Tex *t1 = f0->tex1;
Tex *t2 = f1->tex0;
Tex *t3 = f1->tex1;
double a00 = 1 - f0->alpha;
double a01 = f0->alpha;
double a10 = 1 - f1->alpha;
double a11 = f1->alpha;
if (t0 == t1) { a00 = 0; a01 = 1; }
if (t2 == t3) { a10 = 0; a11 = 1; }
double a0 = p->alpha*a00;
double a1 = p->alpha*a01;
double a2 = (1 - p->alpha)*a10;
double a3 = (1 - p->alpha)*a11;
double k = 0;
if (t0 == t2) k += a00 < a10 ? a00 : a10;
if (t0 == t3) k += a00 < a11 ? a00 : a11;
if (t1 == t2) k += a01 < a10 ? a01 : a10;
if (t1 == t3) k += a01 < a11 ? a01 : a11;
double a4 = p->edge*(1 - k)*EDGE;
unsigned char *c0 = t0->pixels + (y%t0->h*t0->w + x%t0->w)*4;
unsigned char *c1 = t1->pixels + (y%t1->h*t1->w + x%t1->w)*4;
unsigned char *c2 = t2->pixels + (y%t2->h*t2->w + x%t2->w)*4;
unsigned char *c3 = t3->pixels + (y%t3->h*t3->w + x%t3->w)*4;
unsigned char *c4 = black;
for(int i = 0; i < 4; ++i)
*c++ = (*c0++*a0 + *c1++*a1 + *c2++*a2 + *c3++*a3)*(1 - a4) + *c4++*a4;
}
if (filename) imageSave(filename, W, H, image);
return createAnimationFromImageEx(W, H, image, TRUE, TRUE, FALSE, FALSE);
}
void generate() {
// clear
memset(points, 0, sizeof(points));
memset(pixels, 0, sizeof(pixels));
memset(image, 0, sizeof(image));
// generate k
Pixel *p = pixels[0];
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x, ++p) {
p->k[0] = exp(randomFloat()-0.5)*4;
p->k[1] = exp(randomFloat()-0.5);
p->k[2] = (p->k[0] + p->k[1])*0.75;
}
// generate points
for(int i = 0; i < COUNT; ++i) {
Point *pt = &points[i];
pt->x = rand()%W;
pt->y = rand()%H;
pt->color = colorByHSV(randomFloat()*360, 1, 1);
generateFrames(pt->frames);
Pixel *p = getPixel(pt->x, pt->y);
p->point = p->point2 = pt;
p->weight = p->weight2 = randomFloat();
}
// expand points
int found = 1, i = 0;
while(found) {
found = 0;
Pixel *p = pixels[0];
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x, ++p) {
if (!p->point) continue;
if (updatePixel(p, x-1, y, 0)) found = 1;
if (updatePixel(p, x+1, y, 0)) found = 1;
if (updatePixel(p, x, y-1, 1)) found = 1;
if (updatePixel(p, x, y+1, 1)) found = 1;
if (updatePixel(p, x-1, y-1, 2)) found = 1;
if (updatePixel(p, x+1, y-1, 2)) found = 1;
if (updatePixel(p, x-1, y+1, 2)) found = 1;
if (updatePixel(p, x+1, y+1, 2)) found = 1;
}
printf("%d\n", i++); fflush(stdout);
}
// expand points2
p = pixels[0];
for(int y = 0; y < H; ++y)
for(int x = 0; x < W; ++x, ++p) {
updatePixel2(p, x-1, y, 0);
updatePixel2(p, x+1, y, 0);
updatePixel2(p, x, y-1, 1);
updatePixel2(p, x, y+1, 1);
updatePixel2(p, x-1, y-1, 2);
updatePixel2(p, x+1, y-1, 2);
updatePixel2(p, x-1, y+1, 2);
updatePixel2(p, x+1, y+1, 2);
double a = (p->weight2 - p->weight)*0.5 + 0.5;
if (a < 0) a = 0;
if (a > 1) a = 1;
double edge = 1 - (p->weight2 - p->weight)/p->k[2]/8;
if (edge > 1) edge = 1;
if (edge < 0) edge = 0;
if (p->point == p->point2) edge = 0;
p->alpha = a;
p->edge = edge;
}
// generate animation
animationClear(anim);
for(int i = 0; i < FRAMECOUNT; ++i) {
#ifdef SAVE
char filename[1024] = {};
sprintf(filename, SAVE, i);
printf("generate: %s\n", filename); fflush(stdout);
animationInsert(anim, i, generateAnim(i, filename));
#else
printf("generate frame %d\n", i); fflush(stdout);
animationInsert(anim, i, generateAnim(i, NULL));
#endif
}
animationSetFps(anim, 32);
animationSetLoop(anim, TRUE);
}
void loadTexture(const char *filename) {
printf("load texture: %s\n", filename);
Tex *t = &textures[texturesCnt];
if (imageLoad(filename, &t->w, &t->h, &t->pixels))
++texturesCnt;
}
void loadTextures(const char *dirname) {
Directory dir = openDirectoryEx(dirname, "", ".png", FALSE, TRUE, FALSE);
if (dir) {
for(int i = 0; i < directoryGetCount(dir); ++i)
loadTexture(directoryGetFull(dir, i));
closeDirectory(dir);
}
}
void init() {
anim = createAnimationEmpty();
loadTextures("data/textures");
generate();
}
void draw() {
double w = windowGetWidth();
double h = windowGetHeight();
double t = windowGetSeconds();
saveState();
//scale(w/W, h/H);
noStroke();
animationSetFrame(anim, ((int)(t*32))%FRAMECOUNT);
fillTexture(anim, 0, 0, W, H, TRUE);
rect(0, 0, w, h);
//stroke(COLOR_BLACK);
//for(int i = 0; i < COUNT; ++i)
// point(points[i].x, points[i].y);
restoreState();
}
int main() {
windowSetVariableFrameRate();
windowSetResizable(TRUE);
windowSetInit(&init);
windowSetDraw(&draw);
windowRun();
}