#include"node.h" #include #include"img.h" #include #include #include #include #include #include #include #include #include #include"mode.h" #include #include #include #include"linearity.h" static size_t bisect(const void *key, const void *base, size_t nmemb, size_t size, ssize_t(*compar)(const void*, const void*)) { size_t low = 0, high = nmemb; while(low < high) { size_t middle = (low + high) / 2; if(compar((const void*) ((uintptr_t) base + size * middle), key) < 0) { low = middle + 1; } else { high = middle; } } return low; } static ssize_t float_compar(const void *A, const void *B) { float a = *(float*) A; float b = *(float*) B; return (a > b) - (a < b); } static int adjacencycmp(const void *a, const void *b) { size_t v = (uintptr_t) ((CHiAdjacency*) a)[0] - (uintptr_t) ((CHiAdjacency*) b)[0]; return v ? v : (uintptr_t) ((CHiAdjacency*) a)[1] - (uintptr_t) ((CHiAdjacency*) b)[1]; } static void adjacency_add(CHiPubNode *source, CHiPubNode *sink) { CHiNodeGraph *ng = source->ng; if(ng->adjacencyCount == ng->adjacencyCapacity) { ng->adjacencies = realloc(ng->adjacencies, sizeof(CHiAdjacency) * (ng->adjacencyCapacity *= 2)); } ng->adjacencies[ng->adjacencyCount][0] = source; ng->adjacencies[ng->adjacencyCount][1] = sink; ng->adjacencyCount++; qsort(ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp); } static void adjacency_remove(CHiPubNode *source, CHiPubNode *sink) { CHiNodeGraph *ng = source->ng; CHiAdjacency *adj = bsearch(&(CHiAdjacency) {source, sink}, ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp); if(adj) { memmove(adj, adj + 1, sizeof(CHiAdjacency) * (ng->adjacencyCount - (adj - ng->adjacencies) - 1)); ng->adjacencyCount--; } } CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() { CHiNodeGraph *ret = calloc(1, sizeof(*ret)); ret->count = 0; ret->nodes = malloc(sizeof(*ret->nodes) * (ret->capacity = 8)); ret->eventOnStopComplete = NULL; ret->eventOnFrameComplete = NULL; ret->compilationStatus = CUTIHI_COMP_READY; ret->adjacencyCount = 0; ret->adjacencyCapacity = 8; ret->adjacencies = malloc(sizeof(CHiAdjacency) * ret->adjacencyCapacity); return ret; } CUTIVIS CHiValue *CHi_Crawl(CHiValue *v) { while(v->type == CUTIHI_VAL_LINKED || v->type == CUTIHI_VAL_KEYED) { if(v->type == CUTIHI_VAL_LINKED) { v = &v->data.linked.to->sources[v->data.linked.idx]; } else if(v->type == CUTIHI_VAL_KEYED) { v = &v->data.keyed->current; } } return v; } CUTIVIS void CHi_RegisterNode(CHiNodeGraph* ng, CHiPubNode* n) { if(ng->count == ng->capacity) { ng->nodes = realloc(ng->nodes, sizeof(*ng->nodes) * (ng->capacity = ng->capacity * 3 / 2)); } ng->nodes[ng->count++] = n; n->ng = ng; } CUTIVIS void CHi_MakeDirty(CHiNodeGraph *ng, CHiPubNode *n) { for(size_t i = 0; i < ng->count; i++) { } } static int dfs_visit(size_t *resultCount, CHiPubNode ***result, CHiPubNode *n) { if(n->_dfsmark == 2) return 1; else if(n->_dfsmark == 1) return 0; n->_dfsmark = 1; for(size_t s = 0; s < n->sinkCount; s++) { if(n->sinks[s].type == CUTIHI_VAL_LINKED) { if(!dfs_visit(resultCount, result, n->sinks[s].data.linked.to)) { return 0; } } } n->_dfsmark++; (*result)[(*resultCount)++] = n; return 1; } static int topological_sort(CHiNodeGraph *ng) { size_t resultCount = 0; CHiPubNode **result = malloc(sizeof(*result) * ng->capacity); for(size_t i = 0; i < ng->count; i++) { ng->nodes[i]->_dfsmark = 0; } for(size_t i = 0; i < ng->count; i++) { if(!dfs_visit(&resultCount, &result, ng->nodes[i])) { free(result); return 0; } } assert(resultCount == ng->count); free(ng->nodes); ng->nodes = result; return 1; } CUTIVIS int CHi_ConfigureSink(CHiPubNode *n, size_t i, CHiValue v) { if(n->sinks[i].type == CUTIHI_VAL_KEYED) { n->sinks[i].data.keyed->current = v; return 1; } if(v.type == CUTIHI_VAL_LINKED && n == v.data.linked.to) return 0; CHiValue old = n->sinks[i]; if(old.type == CUTIHI_VAL_LINKED) { adjacency_remove(old.data.linked.to, n); } n->sinks[i] = v; if(n->ng && !topological_sort(n->ng)) { n->sinks[i] = old; if(old.type == CUTIHI_VAL_LINKED) { adjacency_add(old.data.linked.to, n); } return 0; } if(v.type == CUTIHI_VAL_LINKED) { adjacency_add(v.data.linked.to, n); } return 1; } CUTIVIS void CHi_MakeKeyframe(CHiNodeGraph *ng, CHiPubNode *n, size_t i) { if(n->sinks[i].type != CUTIHI_VAL_KEYED) { CHiKeyframes *kfs = calloc(1, sizeof(*kfs)); kfs->type = n->sinks[i].type; kfs->count = 1; kfs->times = malloc(sizeof(*kfs->times)); *kfs->times = ng->time; kfs->values = malloc(sizeof(*kfs->values)); memcpy(kfs->values, &n->sinks[i].data, sizeof(CHiValueRaw)); memcpy(&kfs->current, &n->sinks[i], sizeof(CHiValueRaw)); kfs->node = n; n->sinks[i].type = CUTIHI_VAL_KEYED; n->sinks[i].data.keyed = kfs; ng->keyframesList.keyframes = realloc(ng->keyframesList.keyframes, sizeof(*ng->keyframesList.keyframes) * (++ng->keyframesList.count)); ng->keyframesList.keyframes[ng->keyframesList.count - 1] = kfs; } else { CHiKeyframes *kfs = n->sinks[i].data.keyed; float now = ng->time; size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar); if(idx < kfs->count && kfs->times[idx] == now) { kfs->values[idx] = kfs->current.data; } else { kfs->count++; kfs->values = realloc(kfs->values, sizeof(*kfs->values) * kfs->count); kfs->times = realloc(kfs->times, sizeof(*kfs->times) * kfs->count); memmove(kfs->values + idx + 1, kfs->values + idx, sizeof(*kfs->values) * (kfs->count - idx - 1)); memmove(kfs->times + idx + 1, kfs->times + idx, sizeof(*kfs->times) * (kfs->count - idx - 1)); kfs->values[idx] = kfs->current.data; kfs->times[idx] = now; } } } CUTIVIS size_t CHi_MoveKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float to) { CHiValueRaw val = kfs->values[idx]; while(idx < kfs->count - 1 && to > kfs->times[idx + 1]) { memcpy(&kfs->values[idx], &kfs->values[idx + 1], sizeof(*kfs->values)); memcpy(&kfs->times[idx], &kfs->times[idx + 1], sizeof(*kfs->times)); idx++; } while(idx > 0 && to < kfs->times[idx - 1]) { memcpy(&kfs->values[idx], &kfs->values[idx - 1], sizeof(*kfs->values)); memcpy(&kfs->times[idx], &kfs->times[idx - 1], sizeof(*kfs->times)); idx--; } kfs->times[idx] = to; kfs->values[idx] = val; return idx; } CUTIVIS size_t CHi_MoveKeyframeBy(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float dt) { return CHi_MoveKeyframe(ng, kfs, idx, kfs->times[idx] + dt); } CUTIVIS void CHi_DeleteKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx) { memmove(&kfs->times[idx], &kfs->times[idx + 1], (kfs->count - idx - 1) * sizeof(*kfs->times)); memmove(&kfs->values[idx], &kfs->values[idx + 1], (kfs->count - idx - 1) * sizeof(*kfs->values)); kfs->count--; } CUTIVIS size_t CHi_GetClosestKeyframe(CHiNodeGraph *ng, size_t kfsIdx, float t) { CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx]; if(kfs->count == 1) { return 0; } size_t idx = bisect(&t, kfs->times, kfs->count, sizeof(*kfs->times), float_compar); if(idx == 0) { return idx; } if(idx == kfs->count) { return kfs->count - 1; } if(fabs(kfs->times[idx] - t) < fabs(kfs->times[idx - 1] - t)) { return idx; } else { return idx - 1; } } CUTIVIS void CHi_SetExtrapolationMode(CHiNodeGraph *ng, CHiPubNode *n, size_t sinkIdx, CHiExtrapolationMode mode, float* params) { if(n->sinks[sinkIdx].type != CUTIHI_VAL_KEYED) { return; } CHiKeyframes *kfs = n->sinks[sinkIdx].data.keyed; kfs->extrapolationMode = mode; memcpy(kfs->extrapolationParameter, params, sizeof(kfs->extrapolationParameter)); } CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float d) { ng->duration = d; } CUTIVIS int CHi_Hysteresis(CHiPubNode *root) { if(root->ng->compilationStatus != CUTIHI_COMP_READY) return 0; for(size_t s = 0; s < root->sinkCount; s++) { if(root->sinks[s].type == CUTIHI_VAL_LINKED) { CHi_Hysteresis(root->sinks[s].data.linked.to); } } root->Perform(root); return 1; } bool timespec_less(const struct timespec l, const struct timespec r) { if(l.tv_sec == r.tv_sec) { return l.tv_nsec < r.tv_nsec; } else { return l.tv_sec < r.tv_sec; } } struct timespec timespec_sub(const struct timespec l, const struct timespec r) { struct timespec ret; ret.tv_sec = l.tv_sec - r.tv_sec; ret.tv_nsec = l.tv_nsec - r.tv_nsec; if(ret.tv_nsec < 0) { ret.tv_nsec += 1000000000L; ret.tv_sec--; } return ret; } struct timespec timespec_addf(const struct timespec l, const float r) { struct timespec ret; ret.tv_sec = l.tv_sec + floorf(r); ret.tv_nsec = l.tv_nsec + (r - floorf(r)) * 1000000000L; if(ret.tv_nsec > 1000000000L) { ret.tv_sec++; ret.tv_nsec -= 1000000000L; } return ret; } struct timespec timespec_add(const struct timespec l, const struct timespec r) { struct timespec ret; ret.tv_sec = l.tv_sec + r.tv_sec; ret.tv_nsec = l.tv_nsec + r.tv_nsec; if(ret.tv_nsec > 1000000000L) { ret.tv_nsec -= 1000000000L; ret.tv_sec++; } return ret; } float timespecToFloat(const struct timespec t) { return t.tv_sec + t.tv_nsec / 1000000000.f; } struct CompileCtx { CHiNodeGraph *ng; }; void *compile_thread(void *ctx_) { struct CompileCtx *ctx = ctx_; ctx->ng->time = ctx->ng->timedelta = 0; puts("START"); for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) { if(ctx->ng->nodes[nIdx]->Start) { ctx->ng->nodes[nIdx]->Start(ctx->ng->nodes[nIdx]); } else { ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]); } } if(CHi_GetMode() == CUTIHI_MODE_LIVE) { struct timespec start; clock_gettime(CLOCK_MONOTONIC, &start); struct timespec finish = timespec_addf(start, ctx->ng->duration); for(size_t frm = 0; ctx->ng->compilationStatus != CUTIHI_COMP_KILL_YOURSELF; frm++) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); if(ctx->ng->duration != -1 && timespec_less(finish, now)) { break; } struct timespec end = timespec_addf(now, 0.033333333333333333333333); CHi_Time_Set(ctx->ng, timespecToFloat(timespec_sub(now, start))); for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) { ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]); } if(ctx->ng->eventOnFrameComplete) { ctx->ng->eventOnFrameComplete(ctx->ng); } do { clock_gettime(CLOCK_MONOTONIC, &now); } while(timespec_less(now, end)); } } else { __uint128_t diff; for(uint64_t frm = 0; ctx->ng->compilationStatus != CUTIHI_COMP_KILL_YOURSELF && (ctx->ng->duration == -1 || frm < ctx->ng->duration * 30);) { CHi_Time_Set(ctx->ng, frm / 30.f); for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) { ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]); } struct timespec last; clock_gettime(CLOCK_MONOTONIC, &last); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); diff += timespec_sub(now, last).tv_nsec; if(ctx->ng->eventOnFrameComplete) { ctx->ng->eventOnFrameComplete(ctx->ng); } frm++; } } for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) { if(ctx->ng->nodes[nIdx]->Stop) { ctx->ng->nodes[nIdx]->Stop(ctx->ng->nodes[nIdx]); } } puts("END"); if(ctx->ng->eventOnStopComplete) { ctx->ng->eventOnStopComplete(ctx->ng); } ctx->ng->compilationStatus = CUTIHI_COMP_READY; free(ctx); return NULL; } CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng) { ng->compilationStatus = CUTIHI_COMP_RUNNING; struct CompileCtx *ctx = calloc(sizeof(*ctx), 1); ctx->ng = ng; pthread_t thrd; pthread_create(&thrd, NULL, &compile_thread, ctx); } CUTIVIS void CHi_StopCompilation(CHiNodeGraph *ng) { if(ng->compilationStatus == CUTIHI_COMP_RUNNING) { ng->compilationStatus = CUTIHI_COMP_KILL_YOURSELF; } } static int image_perform(CHiPubNode *node) { if(node->clean) return 1; node->sources->type = CUTIHI_VAL_SAMPLE; if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample); struct sail_image *simg; SAIL_TRY(sail_load_from_file(node->sinks[0].data.text, &simg)); struct sail_image *cimg; sail_convert_image(simg, SAIL_PIXEL_FORMAT_BPP64_BGRA, &cimg); sail_destroy_image(simg); CHiImage *img = CHi_Image_New(2, 4, (cimg->bytes_per_line + 15) & ~15, cimg->width, cimg->height, NULL); CHi_Restride(cimg->pixels, img->data16, cimg->bytes_per_line, img->stride, img->height); node->sources->data.sample = img; for(size_t y = 0; y < img->height; y++) { for(size_t x = 0; x < img->stride; x += 16) { __m128i pixels = _mm_load_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x)); pixels = apply_gamma_epi16(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f)); _mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixels); } } sail_destroy_image(cimg); node->clean = 0; return 1; err: node->sources->data.sample = NULL; return 0; } CUTIVIS CHiPubNode *CHi_Image() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CIma','ge '); n->Start = n->Stop = NULL; n->Perform = image_perform; n->clean = 0; n->sinkCount = 1; n->sinks = calloc(sizeof(*n->sinks), 1); n->sourceCount = 1; n->sources = calloc(sizeof(*n->sources), 1); return n; } static int embed_perform(CHiPubNode *node) { if(node->clean) return 1; node->sources[0].type = CUTIHI_VAL_SAMPLE; CHiImage *main = CHi_Crawl(&node->sinks[0])->data.sample; if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample); CHiImage *dest = node->sources->data.sample = CHi_Image_New(2, 4, main->stride, main->width, main->height, NULL); memcpy(dest->data16, main->data16, main->stride * main->height); for(int sid = 0; sid < CUTIHI_EMBED_MAX_SMALLS; sid++) { CHiImage *sub = CHi_Crawl(&node->sinks[1 + sid * 3])->data.sample; if(!sub) continue; int sy = 0; int dy = (int16_t) CHi_Crawl(&node->sinks[2 + sid * 3])->data.vec4[1]; if(dy < 0) { sy = -dy; dy = 0; } for(; sy < sub->height && dy < dest->height; sy++, dy++) { int sx = 0; int dx = (int16_t) CHi_Crawl(&node->sinks[2 + sid * 3])->data.vec4[0]; if(dx < 0) { sx = -dx; dx = 0; } for(; sx < sub->width && dx < dest->width; sx += 2, dx += 2) { __m128i bottom = _mm_loadu_si128((__m128i*) ((uintptr_t) dest->data16 + dy * dest->stride + dx * 8)); __m128i top = _mm_loadu_si128((__m128i*) ((uintptr_t) sub->data16 + sy * sub->stride + sx * 8)); __m128i alpha = _mm_shuffle_epi8(top, _mm_set_epi8(15, 14, 15, 14, 15, 14, 15, 14, 7, 6, 7, 6, 7, 6, 7, 6)); __m128i invAlpha = _mm_sub_epi16(_mm_set1_epi16(0xFFFF), alpha); __m128i result = _mm_add_epi16(_mm_mulhi_epu16(top, alpha), _mm_mulhi_epu16(bottom, invAlpha)); _mm_storeu_si128((__m128i*) ((uintptr_t) dest->data16 + dy * dest->stride + dx * 8), result); } } } node->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_Embed() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CEmb','ed '); n->Start = n->Stop = NULL; n->Perform = embed_perform; n->clean = 0; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 1 + 3 * CUTIHI_EMBED_MAX_SMALLS); for(int i = 0; i < CUTIHI_EMBED_MAX_SMALLS; i++) { n->sinks[2 + i * 3].type = CUTIHI_VAL_VEC4; n->sinks[2 + i * 3].data.vec4[0] = 0; n->sinks[2 + i * 3].data.vec4[1] = 0; n->sinks[3 + i * 3].type = CUTIHI_VAL_VEC4; n->sinks[3 + i * 3].data.vec4[0] = 1; } n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1); return n; } static int constantsample_perform(CHiPubNode *node) { if(node->clean) return 1; node->sources[0].type = CUTIHI_VAL_SAMPLE; if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample); CHiValue *sink = CHi_Crawl(&node->sinks[0]); CHiImage *img = CHi_Image_New(2, 4, 8 * 16, 16, 16, NULL); for(int i = 0; i < 256; i++) { img->data16[i * 4 + 0] = sink->data.vec4[2] * 65535; img->data16[i * 4 + 1] = sink->data.vec4[1] * 65535; img->data16[i * 4 + 2] = sink->data.vec4[0] * 65535; img->data16[i * 4 + 3] = 65535; } node->sources->data.sample = img; node->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_ConstantSample() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CCns','tCol'); n->Start = n->Stop = NULL; n->Perform = constantsample_perform; n->clean = 0; n->sinkCount = 1; n->sinks = calloc(sizeof(*n->sinks), 1); n->sourceCount = 1; n->sources = calloc(sizeof(*n->sources), 1); return n; } static int modulate_perform(CHiPubNode *node) { if(node->clean) return 1; node->sources[0].type = CUTIHI_VAL_SAMPLE; if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample); node->sources->data.sample = CHi_Image_New(2, 4, 8 * 16, 16, 16, NULL); node->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_Modulate() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CMod','ulat'); n->Start = n->Stop = NULL; n->Perform = modulate_perform; n->clean = 0; n->sinkCount = 4; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount); n->sourceCount = 1; n->sources = calloc(sizeof(*n->sources), n->sourceCount); return n; } static void update_keyed_values(CHiNodeGraph *ng) { for(size_t kfsIdx = 0; kfsIdx < ng->keyframesList.count; kfsIdx++) { CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx]; kfs->current.type = kfs->type; float now = ng->time; size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar); if(idx == 0) { kfs->current.data = kfs->values[idx]; if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) { kfs->current.data.vec4[0] += (now - kfs->times[0]) * kfs->extrapolationParameter[0]; kfs->current.data.vec4[1] += (now - kfs->times[0]) * kfs->extrapolationParameter[1]; kfs->current.data.vec4[2] += (now - kfs->times[0]) * kfs->extrapolationParameter[2]; kfs->current.data.vec4[3] += (now - kfs->times[0]) * kfs->extrapolationParameter[3]; } } else if(idx == kfs->count) { kfs->current.data = kfs->values[idx - 1]; if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) { kfs->current.data.vec4[0] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[0]; kfs->current.data.vec4[1] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[1]; kfs->current.data.vec4[2] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[2]; kfs->current.data.vec4[3] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[3]; } } else { if(kfs->type == CUTIHI_VAL_VEC4) { float alpha = (now - kfs->times[idx - 1]) / (kfs->times[idx] - kfs->times[idx - 1]); kfs->current.data.vec4[0] = kfs->values[idx - 1].vec4[0] + (kfs->values[idx].vec4[0] - kfs->values[idx - 1].vec4[0]) * alpha; kfs->current.data.vec4[1] = kfs->values[idx - 1].vec4[1] + (kfs->values[idx].vec4[1] - kfs->values[idx - 1].vec4[1]) * alpha; kfs->current.data.vec4[2] = kfs->values[idx - 1].vec4[2] + (kfs->values[idx].vec4[2] - kfs->values[idx - 1].vec4[2]) * alpha; kfs->current.data.vec4[3] = kfs->values[idx - 1].vec4[3] + (kfs->values[idx].vec4[3] - kfs->values[idx - 1].vec4[3]) * alpha; } else { kfs->current.data = kfs->values[idx - 1]; } } } } static int time_perform(CHiPubNode *node) { node->sources->type = CUTIHI_VAL_VEC4; node->sources->data.vec4[0] = node->ng->time; node->clean = 0; return 1; } CUTIVIS void CHi_Time_Set(CHiNodeGraph *ng, float f) { ng->timedelta = f - ng->time; ng->time = f; update_keyed_values(ng); } CUTIVIS float CHi_Time_Get(CHiNodeGraph *ng) { return ng->time; } CUTIVIS float CHi_Time_GetDelta(CHiNodeGraph *ng) { return ng->timedelta; } CUTIVIS CHiPubNode *CHi_Time() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CTim','e '); n->Start = n->Stop = NULL; n->Perform = time_perform; n->clean = 0; n->sinkCount = 0; n->sinks = NULL; n->sourceCount = 1; n->sources = calloc(sizeof(*n->sources), 1); return n; } static PangoFontMap *pfontmap; static PangoContext *pcontext; static PangoFontDescription * pfontdesc; static PangoLayout *playout; static int text_perform(CHiPubNode *n) { if(n->clean) return 1; if(!pfontmap) { pfontmap = pango_ft2_font_map_new(); pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(pfontmap), 72, 72); pcontext = pango_font_map_create_context(pfontmap); pango_context_set_language(pcontext, pango_language_from_string("en_US")); pango_context_set_base_dir(pcontext, PANGO_DIRECTION_LTR); pfontdesc = pango_font_description_from_string("Open Sans 48"); playout = pango_layout_new(pcontext); pango_layout_set_font_description(playout, pfontdesc); } pango_layout_set_markup(playout, CHi_Crawl(&n->sinks[0])->data.text, -1); pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(pfontmap), CHi_Crawl(&n->sinks[2])->data.vec4[0], CHi_Crawl(&n->sinks[2])->data.vec4[0]); PangoRectangle extents; pango_layout_get_extents(playout, NULL, &extents); n->sources[0].type = CUTIHI_VAL_SAMPLE; if(n->sources->data.sample) CHi_Image_Free(n->sources->data.sample); size_t width = (PANGO_PIXELS(extents.width) + 15) & ~15; CHiImage *chiret = CHi_Image_New(2, 4, 8 * width, width, PANGO_PIXELS(extents.height), NULL); n->sources->data.sample = chiret; FT_Bitmap bmp = {}; FT_Bitmap_New(&bmp); bmp.width = chiret->width; bmp.rows = chiret->height; bmp.buffer = calloc(bmp.width, bmp.rows); bmp.pitch = chiret->width; bmp.pixel_mode = FT_PIXEL_MODE_GRAY; bmp.num_grays = 256; pango_ft2_render_layout(&bmp, playout, PANGO_PIXELS(extents.x) + (PANGO_PIXELS(extents.width) + 15) % 16 / 4, PANGO_PIXELS(extents.y)); __m128i ones = _mm_set1_epi64x( (((size_t) (n->sinks[1].data.vec4[2] * 255) % 256) << 0) | (((size_t) (n->sinks[1].data.vec4[1] * 255) % 256) << 16) | (((size_t) (n->sinks[1].data.vec4[0] * 255) % 256) << 32) | 0x0100000000000000 ); for(size_t p = 0; p < bmp.width * bmp.rows; p += 2) { __m128i alphad0 = _mm_mullo_epi16(ones, _mm_set_epi16(bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 0], bmp.buffer[p + 0], bmp.buffer[p + 0], bmp.buffer[p + 0])); _mm_stream_si128((__m128i*) &chiret->data16[p * 4], alphad0); } free(bmp.buffer); n->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_Text() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CTex','t '); n->Start = n->Stop = NULL; n->Perform = text_perform; n->clean = 0; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 3); n->sinks[2].type = CUTIHI_VAL_VEC4; n->sinks[2].data.vec4[0] = 72; n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1); return n; } static int mixer_perform(CHiPubNode *n) { n->sources[0].type = CUTIHI_VAL_SAMPLE; if(n->sources[0].data.sample) { CHi_Image_Free(n->sources[0].data.sample); n->sources[0].data.sample = NULL; } CHiImage *src0 = CHi_Crawl(&n->sinks[0])->data.sample; CHiImage *src1 = CHi_Crawl(&n->sinks[1])->data.sample; if(!src0 && !src1) { return 1; } assert(src0->width == src1->width && src0->height == src1->height); n->sources[0].data.sample = CHi_Image_New(2, 1, (src0->stride + 15) & ~15, src0->width, src0->height, NULL); for(size_t b = 0; b < src0->stride; b += 16) { __m128i a0 = src0 ? _mm_load_si128((__m128i*) ((uintptr_t) src0->data16 + b)) : _mm_setzero_si128(); __m128i a1 = src1 ? _mm_load_si128((__m128i*) ((uintptr_t) src1->data16 + b)) : _mm_setzero_si128(); _mm_stream_si128((__m128i*) ((uintptr_t) n->sources[0].data.sample->data16 + b), _mm_adds_epi16(a0, a1)); } n->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_Mixer() { CHiPubNode *n = calloc(1, sizeof(*n)); n->type = CUTIHI_T('CMix','er '); n->Start = n->Stop = NULL; n->Perform = mixer_perform; n->clean = 0; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 2); n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1); return n; } static int preview_perform(CHiPubNode *n) { return 1; } CUTIVIS CHiPubNode *CHi_Preview() { CHiPubNode *n = malloc(sizeof(*n)); n->type = CUTIHI_T('CPre','view'); n->Start = n->Stop = NULL; n->Perform = preview_perform; n->clean = 0; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 1); n->sinks[0].type = CUTIHI_VAL_SAMPLE; n->sinks[0].data.sample = NULL; n->sources = NULL; n->sourceCount = 0; return n; } static void save_chival(CHiNodeGraph *ng, CHiSaveWriter writer, CHiValType type, CHiValueRaw data, void *ud) { if(type == CUTIHI_VAL_TEXT) { size_t len = strlen(data.text); writer(ud, &(uint32_t) {len}, sizeof(uint32_t)); writer(ud, data.text, len); } else if(type == CUTIHI_VAL_VEC4) { writer(ud, data.vec4, sizeof(data.vec4)); } else if(type == CUTIHI_VAL_LINKED) { size_t index; for(index = 0; index < ng->count; index++) { if(ng->nodes[index] == data.linked.to) { break; } } assert(index < ng->count); writer(ud, &(uint64_t) {index}, sizeof(uint64_t)); } else if(type == CUTIHI_VAL_KEYED) { size_t index; for(index = 0; index < ng->keyframesList.count; index++) { if(ng->keyframesList.keyframes[index] == data.keyed) { break; } } assert(index < ng->count); writer(ud, &(uint64_t) {index}, sizeof(uint64_t)); } } CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud) { writer(ud, "\x71\x74\xCE\xA0", 4); writer(ud, &(float) {ng->duration}, sizeof(float)); writer(ud, &(float) {ng->time}, sizeof(float)); writer(ud, &(uint64_t) {ng->count}, sizeof(uint64_t)); for(size_t i = 0; i < ng->count; i++) { CHiPubNode *node = ng->nodes[i]; writer(ud, &(uint16_t) {node->type}, sizeof(uint16_t)); node->Save(node, ud, writer); writer(ud, &(uint16_t) {node->sinkCount}, sizeof(uint16_t)); for(size_t sink = 0; sink < node->sinkCount; sink++) { writer(ud, &(uint16_t) {node->sinks[sink].type}, sizeof(uint16_t)); save_chival(ng, writer, node->sinks[sink].type, node->sinks[sink].data, ud); } } writer(ud, &(uint64_t) {ng->keyframesList.count}, sizeof(uint64_t)); for(size_t i = 0; i < ng->keyframesList.count; i++) { CHiKeyframes *kfs = ng->keyframesList.keyframes[i]; writer(ud, &(uint16_t) {kfs->type}, sizeof(uint16_t)); writer(ud, &(uint64_t) {kfs->count}, sizeof(uint64_t)); writer(ud, kfs->times, sizeof(*kfs->times) * kfs->count); for(size_t k = 0; k < kfs->count; k++) { save_chival(ng, writer, kfs->type, kfs->values[k], ud); } writer(ud, &(uint16_t) {kfs->extrapolationMode}, sizeof(uint16_t)); writer(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter)); } return 0; }