Blob Blame History Raw

#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();
}