#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& 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(img_a, img_a + width * height * 4); auto i2 = std::vector(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(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 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; }