atomorph/src/EpsilonHook.cpp

143 lines
4.2 KiB
C++

#include "morph.h"
#define export extern "C" [[maybe_unused]]
static bool set_morph_frame(am::morph* morph, size_t frame, uint16_t width, const std::vector<uint8_t>& image) {
auto sz = image.size();
unsigned char r = 0, g = 0, b = 0, a = 0;
size_t pixel = 0;
if (width > UINT16_MAX)
return false;
for (size_t j = 0; j < sz; ++j) {
switch (j % 4) {
case 0: r = image.at(j); break;
case 1: g = image.at(j); break;
case 2: b = image.at(j); break;
default: {
a = image.at(j);
if (a == 0) {
pixel++;
continue;
}
morph->add_pixel(frame, am::create_pixel(pixel % width, pixel / width, r, g, b, a));
pixel++;
break;
}
}
}
return true;
}
export am::morph* create_morph(uint16_t width, uint16_t height, uint8_t img_a[],
uint8_t img_b[]) {
auto* morph = new am::morph();
morph->set_blob_delimiter(am::HSP);
morph->set_blob_max_size(SIZE_MAX);
morph->set_blob_min_size(1);
morph->set_blob_box_grip(UINT16_MAX);
morph->set_blob_box_samples(100);
morph->set_blob_threshold(1.0);
morph->set_blob_number(1);
morph->set_seed(0);
morph->set_blob_rgba_weight(1);
morph->set_blob_size_weight(0);
morph->set_blob_xy_weight(0);
morph->set_degeneration(10000);
morph->set_density(2); // Higher than 1 sets fluid to 0.
morph->set_motion(am::SPLINE);
morph->set_fading(am::PERLIN);
morph->set_threads(16);
morph->set_cycle_length(100000);
morph->set_feather(0);
morph->set_keep_background(0);
morph->set_finite(1);
morph->set_show_blobs(am::TEXTURE);
morph->set_fluid(0); // Higher than 0 sets density to 1.
morph->set_resolution(width, height);
auto i1 = std::vector<uint8_t>(img_a, img_a + width * height * 4);
auto i2 = std::vector<uint8_t>(img_b, img_b + width * height * 4);
auto success = set_morph_frame(morph, 0, width, i1) && set_morph_frame(morph, 1, width, i2);
if (!success) {
return nullptr;
}
morph->compute();
while (true) {
morph->suspend();
morph->synchronize();
morph->compute();
auto state = morph->get_state();
if (state > am::STATE_BLOB_UNIFICATION) {
break;
}
}
return morph;
}
export void run_blob_matching(am::morph* morph, size_t ms) {
std::chrono::steady_clock::time_point start, now;
start = std::chrono::steady_clock::now();
while (true) {
morph->suspend();
morph->synchronize();
morph->compute();
auto state = morph->get_state();
if (state < am::STATE_BLOB_MATCHING) {
continue;
}
now = std::chrono::steady_clock::now();
if ((size_t)std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() >= ms) {
morph->next_state();
break;
}
}
auto state = morph->get_state();
while (state < am::STATE_ATOM_MORPHING) {
morph->suspend();
morph->synchronize();
morph->compute();
state = morph->get_state();
}
}
export void finish_atom_matching(am::morph* morph) {
morph->suspend();
auto state = morph->get_state();
while (state < am::STATE_ATOM_MORPHING) {
morph->suspend();
morph->synchronize();
morph->compute();
state = morph->get_state();
}
morph->next_state();
}
export void get_frame(am::morph* morph, size_t frame, size_t total_frames, uint32_t width, uint32_t height,
uint8_t out[]) {
double t = morph->get_time(frame, total_frames);
std::vector<am::pixel> pixels;
morph->get_pixels(t, &pixels);
auto size = width * height * 4;
while (!pixels.empty()) {
am::pixel px = pixels.back();
pixels.pop_back();
size_t pos = ((size_t)px.y * width + (size_t)px.x) * 4;
if (pos >= size)
continue;
out[pos + 0] = px.c.r;
out[pos + 1] = px.c.g;
out[pos + 2] = px.c.b;
out[pos + 3] = px.c.a;
}
}
export void delete_morph(am::morph* morph) { delete morph; }