181 lines
7.8 KiB
C
181 lines
7.8 KiB
C
/* Composición de capas, Onion Skin y reproductor. */
|
|
#include <math.h>
|
|
#include "tgb_test.h"
|
|
|
|
static tgb_context *g_ctx;
|
|
static char g_root[200];
|
|
|
|
static tgb_document *new_doc(int w, int h, int fps) {
|
|
tgb_document *d = NULL;
|
|
CHECK_OK(tgb_document_create(g_ctx, w, h, fps, &d));
|
|
return d;
|
|
}
|
|
|
|
TEST(flatten_layers) {
|
|
tgb_document *d = new_doc(8, 8, 12);
|
|
CHECK_OK(tgb_layer_add(d, 1, "Arriba", NULL));
|
|
CHECK_OK(paint_px(d, 0, 0, 1, 1, RED));
|
|
CHECK_OK(paint_px(d, 0, 1, 1, 1, BLUE));
|
|
tgb_canvas *out = tgb_canvas_create(8, 8);
|
|
CHECK_OK(tgb_render_flatten(d, 0, out));
|
|
CHECK(is_px(out, 1, 1, BLUE)); /* la capa de arriba tapa a la de abajo */
|
|
CHECK_OK(tgb_layer_set_visible(d, 1, false));
|
|
CHECK_OK(tgb_render_flatten(d, 0, out));
|
|
CHECK(is_px(out, 1, 1, RED)); /* capa oculta: no cuenta */
|
|
CHECK_OK(tgb_layer_set_visible(d, 1, true));
|
|
CHECK_OK(tgb_layer_set_opacity(d, 1, 128));
|
|
CHECK_OK(tgb_render_flatten(d, 0, out));
|
|
tgb_color p = tgb_canvas_get(out, 1, 1);
|
|
CHECK(p.a == 255 && abs(p.r - 127) <= 2 && abs(p.b - 128) <= 2); /* mezcla 50 % */
|
|
CHECK_OK(tgb_layer_move(d, 1, 0));
|
|
CHECK_OK(tgb_layer_set_opacity(d, 0, 255));
|
|
CHECK_OK(tgb_render_flatten(d, 0, out));
|
|
CHECK(is_px(out, 1, 1, RED)); /* cambiar el orden cambia el resultado */
|
|
CHECK_OK(tgb_render_flatten(d, 0, out));
|
|
CHECK_EQ(tgb_canvas_dirty(out).w, 0);
|
|
tgb_canvas *small = tgb_canvas_create(4, 4);
|
|
CHECK_STATUS(tgb_render_flatten(d, 0, small), TGB_ERR_RANGE);
|
|
CHECK_STATUS(tgb_render_flatten(d, 5, out), TGB_ERR_RANGE);
|
|
CHECK_STATUS(tgb_render_flatten(NULL, 0, out), TGB_ERR_INVALID_ARG);
|
|
tgb_canvas_release(small); tgb_canvas_release(out);
|
|
tgb_document_destroy(d);
|
|
}
|
|
|
|
TEST(onion_skin) {
|
|
tgb_document *d = new_doc(8, 8, 12);
|
|
CHECK_OK(tgb_frame_add(d, 1, NULL));
|
|
CHECK_OK(tgb_frame_add(d, 2, NULL));
|
|
CHECK_OK(paint_px(d, 0, 0, 2, 2, RED)); /* fotograma 0: rojo */
|
|
CHECK_OK(paint_px(d, 2, 0, 5, 5, GREEN)); /* fotograma 2: verde */
|
|
tgb_onion_settings on; tgb_onion_defaults(&on);
|
|
CHECK(!on.enabled);
|
|
on.tint = false; on.opacity_before = 0.4f; on.opacity_after = 0.5f; on.falloff = 0.5f;
|
|
tgb_canvas *out = tgb_canvas_create(8, 8);
|
|
tgb_color none = {0, 0, 0, 0};
|
|
/* desactivado: no se ve nada del resto */
|
|
CHECK_OK(tgb_render_view(d, 1, &on, none, out));
|
|
CHECK(tgb_canvas_is_empty(out));
|
|
on.enabled = true;
|
|
CHECK_OK(tgb_render_view(d, 1, &on, none, out));
|
|
tgb_color pr = tgb_canvas_get(out, 2, 2), pg = tgb_canvas_get(out, 5, 5);
|
|
CHECK(pr.r == 255 && pr.g == 0 && abs(pr.a - 102) <= 1); /* anterior: 40 % */
|
|
CHECK(pg.g == 255 && abs(pg.a - 128) <= 1); /* posterior: 50 % */
|
|
/* tinte: el color original se mezcla con el tinte */
|
|
on.tint = true; on.tint_before = BLUE;
|
|
CHECK_OK(tgb_render_view(d, 1, &on, none, out));
|
|
pr = tgb_canvas_get(out, 2, 2);
|
|
CHECK(pr.b > 100 && pr.r < 255);
|
|
/* dos fotogramas hacia atrás con atenuación */
|
|
on.tint = false; on.frames_before = 2; on.frames_after = 0;
|
|
CHECK_OK(tgb_render_view(d, 2, &on, none, out));
|
|
CHECK(abs(tgb_canvas_get(out, 2, 2).a - 51) <= 1); /* 0,4 * 0,5 */
|
|
CHECK(is_px(out, 5, 5, GREEN)); /* el fotograma actual va encima, sin transparencia */
|
|
/* sin fotogramas anteriores al primero */
|
|
CHECK_OK(tgb_render_view(d, 0, &on, none, out));
|
|
CHECK(is_px(out, 2, 2, RED));
|
|
CHECK_EQ(count_alpha(out), 1);
|
|
/* fondo opaco */
|
|
tgb_color white = {255, 255, 255, 255};
|
|
on.frames_after = 2; on.frames_before = 0;
|
|
CHECK_OK(tgb_render_view(d, 1, &on, white, out));
|
|
CHECK(is_px(out, 0, 0, white));
|
|
CHECK(tgb_canvas_get(out, 5, 5).g == 255 && tgb_canvas_get(out, 5, 5).r > 100);
|
|
/* validación de ajustes */
|
|
tgb_onion_settings bad = on;
|
|
bad.frames_before = 99; bad.frames_after = -3; bad.opacity_before = 7.0f; bad.opacity_after = NAN; bad.falloff = -1;
|
|
CHECK_OK(tgb_onion_validate(&bad));
|
|
CHECK(bad.frames_before == 8 && bad.frames_after == 0 && bad.opacity_before == 1.0f && bad.opacity_after == 0.0f && bad.falloff == 0.0f);
|
|
CHECK_STATUS(tgb_onion_validate(NULL), TGB_ERR_INVALID_ARG);
|
|
tgb_canvas_release(out);
|
|
tgb_document_destroy(d);
|
|
}
|
|
|
|
TEST(player_loop_once_pingpong) {
|
|
tgb_document *d = new_doc(8, 8, 10); /* 10 fps = 100 ms por fotograma */
|
|
CHECK_OK(tgb_frame_add(d, 1, NULL)); CHECK_OK(tgb_frame_add(d, 2, NULL));
|
|
tgb_player *p = tgb_player_create(d);
|
|
CHECK(p != NULL);
|
|
CHECK_EQ(tgb_player_update(p, 500), 0); /* parado: no avanza */
|
|
tgb_player_play(p);
|
|
CHECK(tgb_player_is_playing(p));
|
|
CHECK_EQ(tgb_player_update(p, 50), 0);
|
|
CHECK_EQ(tgb_player_update(p, 60), 1);
|
|
CHECK_EQ(tgb_player_update(p, 100), 2);
|
|
CHECK_EQ(tgb_player_update(p, 100), 0); /* bucle */
|
|
tgb_player_pause(p);
|
|
CHECK_EQ(tgb_player_update(p, 1000), 0);
|
|
tgb_player_stop(p);
|
|
CHECK(!tgb_player_is_playing(p));
|
|
/* una vez */
|
|
tgb_player_set_mode(p, TGB_PLAY_ONCE);
|
|
tgb_player_play(p);
|
|
CHECK_EQ(tgb_player_update(p, 100), 1);
|
|
CHECK_EQ(tgb_player_update(p, 100), 2);
|
|
CHECK_EQ(tgb_player_update(p, 100), 2);
|
|
CHECK(!tgb_player_is_playing(p));
|
|
tgb_player_play(p); /* volver a dar a play reinicia */
|
|
CHECK(tgb_player_is_playing(p) && tgb_player_frame(p) == 0);
|
|
/* ping-pong: 0,1,2,1,0,1,2 */
|
|
tgb_player_stop(p);
|
|
tgb_player_set_mode(p, TGB_PLAY_PINGPONG);
|
|
tgb_player_play(p);
|
|
size_t expect[] = {1, 2, 1, 0, 1, 2};
|
|
for (int i = 0; i < 6; i++) CHECK_EQ(tgb_player_update(p, 100), expect[i]);
|
|
/* velocidad doble */
|
|
tgb_player_stop(p);
|
|
tgb_player_set_mode(p, TGB_PLAY_LOOP);
|
|
tgb_player_set_speed(p, 2.0f);
|
|
tgb_player_play(p);
|
|
CHECK_EQ(tgb_player_update(p, 50), 1);
|
|
tgb_player_destroy(p);
|
|
CHECK_OK(0);
|
|
tgb_document_destroy(d);
|
|
}
|
|
|
|
TEST(player_durations_range_and_edge_cases) {
|
|
tgb_document *d = new_doc(8, 8, 10);
|
|
CHECK_OK(tgb_frame_add(d, 1, NULL)); CHECK_OK(tgb_frame_add(d, 2, NULL));
|
|
CHECK_OK(tgb_frame_set_duration(d, 1, 300)); /* duraciones: 100, 300, 100 */
|
|
tgb_player *p = tgb_player_create(d);
|
|
tgb_player_play(p);
|
|
CHECK_EQ(tgb_player_update(p, 150), 1);
|
|
CHECK_EQ(tgb_player_update(p, 240), 1); /* 390 ms: aún en el fotograma de 300 ms (100..400) */
|
|
CHECK_EQ(tgb_player_update(p, 20), 2); /* 410 ms: ya en el tercero */
|
|
/* un dt gigante no cuelga ni sale del rango */
|
|
size_t f = tgb_player_update(p, 1e12);
|
|
CHECK(f < 3);
|
|
CHECK_EQ(tgb_player_update(p, -5), f); /* dt negativo se ignora */
|
|
/* rango 1..2 */
|
|
CHECK_STATUS(tgb_player_set_range(p, 2, 1), TGB_ERR_RANGE);
|
|
CHECK_STATUS(tgb_player_set_range(p, 0, 9), TGB_ERR_RANGE);
|
|
CHECK_OK(tgb_player_set_range(p, 1, 2));
|
|
tgb_player_stop(p);
|
|
CHECK_EQ(tgb_player_frame(p), 1);
|
|
tgb_player_play(p);
|
|
CHECK_EQ(tgb_player_update(p, 300), 2);
|
|
CHECK_EQ(tgb_player_update(p, 100), 1); /* vuelve al inicio del rango, no al 0 */
|
|
tgb_player_clear_range(p);
|
|
CHECK_STATUS(tgb_player_seek(p, 7), TGB_ERR_RANGE);
|
|
CHECK_OK(tgb_player_seek(p, 2));
|
|
CHECK_EQ(tgb_player_frame(p), 2);
|
|
/* el documento cambia mientras se reproduce */
|
|
CHECK_OK(tgb_frame_delete(d, 2));
|
|
CHECK(tgb_player_update(p, 10) < 2);
|
|
tgb_player_set_speed(p, -1.0f); tgb_player_set_speed(p, 1000.0f); /* se recortan sin fallar */
|
|
tgb_player_destroy(p);
|
|
tgb_player_destroy(NULL);
|
|
CHECK(tgb_player_create(NULL) == NULL);
|
|
tgb_document_destroy(d);
|
|
}
|
|
|
|
int main(void) {
|
|
g_ctx = tgb_test_context(g_root, sizeof g_root);
|
|
RUN(flatten_layers);
|
|
RUN(onion_skin);
|
|
RUN(player_loop_once_pingpong);
|
|
RUN(player_durations_range_and_edge_cases);
|
|
tgb_context_destroy(g_ctx);
|
|
tgb_test_rmtree(g_root);
|
|
TEST_SUMMARY();
|
|
}
|