Exemplo n.º 1
0
static bool node_visitor_dimensions(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref, int32_t node_id,
                                    bool * quit, bool * skip_outbound_paths, void * custom_data)
{

    struct flow_node * n = &(*graph_ref)->nodes[node_id];

    int32_t outbound_edges = flow_graph_get_edge_count(c, *graph_ref, node_id, false, flow_edgetype_null, false, true);
    if (outbound_edges == 0) {
        return true; // Endpoint node - no need.
    }
    if (!flow_node_has_dimensions(c, *graph_ref, node_id)) {
        if (!flow_node_update_state(c, *graph_ref, node_id)) {
            FLOW_error_return(c);
        }

        // If input nodes are populated
        if ((n->state & flow_node_state_InputDimensionsKnown) > 0) {
            if (!flow_job_populate_dimensions_for_node(c, job, *graph_ref, node_id, (bool)custom_data)) {
                FLOW_error_return(c);
            }
        }
        if (!flow_node_has_dimensions(c, *graph_ref, node_id)) {
            // We couldn't populate this edge, so we sure can't populate others in this direction.
            // Stop this branch of recursion
            *skip_outbound_paths = true;
        } else {
            flow_job_notify_graph_changed(c, job, *graph_ref);
        }
    }
    return true;
}
Exemplo n.º 2
0
static bool node_visitor_execute(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref, int32_t node_id,
                                 bool * quit, bool * skip_outbound_paths, void * custom_data)
{

    if (!flow_node_update_state(c, *graph_ref, node_id)) {
        FLOW_error_return(c);
    }
    struct flow_node * n = &(*graph_ref)->nodes[node_id];

    if (!flow_job_node_is_executed(c, job, *graph_ref, node_id) && n->state == flow_node_state_ReadyForExecution) {
        uint64_t now = flow_get_high_precision_ticks();
        if (!flow_node_execute(c, job, *graph_ref, node_id)) {
            FLOW_error_return(c);
        } else {
            (*graph_ref)->nodes[node_id].ticks_elapsed += (int32_t)(flow_get_high_precision_ticks() - now);
            n->state = (flow_node_state)(n->state | flow_node_state_Executed);
            flow_job_notify_node_complete(c, job, *graph_ref, node_id);
        }
    }
    if (!flow_job_node_is_executed(c, job, *graph_ref, node_id)) {
        // If we couldn't complete this node yet, end this branch.
        *skip_outbound_paths = true;
    } else {
        flow_job_notify_graph_changed(c, job, *graph_ref);
    }
    return true;
}
Exemplo n.º 3
0
static bool node_visitor_post_optimize_flatten(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref,
                                               int32_t node_id, bool * quit, bool * skip_outbound_paths,
                                               void * custom_data)
{

    if (!flow_node_update_state(c, *graph_ref, node_id)) {
        FLOW_error_return(c);
    }
    struct flow_node * n = &(*graph_ref)->nodes[node_id];

    // If input nodes are populated
    if (n->state == flow_node_state_ReadyForPostOptimizeFlatten) {
        if (!flow_node_post_optimize_flatten(c, graph_ref, node_id)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_validate(c, *graph_ref)) {
            FLOW_error_return(c);
        }
        *quit = true;
        *((bool *)custom_data) = true;
    } else if ((n->state & flow_node_state_InputDimensionsKnown) == 0) {
        // we can't flatten past missing dimensions
        *skip_outbound_paths = true;
    }
    return true;
}
Exemplo n.º 4
0
bool load_image(flow_c * c, char * checksum, struct flow_bitmap_bgra ** ref, void * bitmap_owner)
{
    char filename[2048];
    if (!create_relative_path(c, false, filename, 2048, "/visuals/%s.png", checksum)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    struct flow_job * job = flow_job_create(c);
    if (job == NULL) {
        FLOW_error_return(c);
    }
    size_t bytes_count;
    uint8_t * bytes = read_all_bytes(c, &bytes_count, filename);
    if (bytes == NULL) {
        FLOW_error_return(c);
    }
    struct flow_io * input = flow_io_create_from_memory(c, flow_io_mode_read_seekable, bytes, bytes_count, job, NULL);
    if (input == NULL) {
        FLOW_error_return(c);
    }
    if (!flow_job_add_io(c, job, input, 0, FLOW_INPUT)) {
        FLOW_error_return(c);
    }

    struct flow_graph * g = flow_graph_create(c, 10, 10, 200, 2.0);
    if (g == NULL) {
        FLOW_add_to_callstack(c);
        return false;
    }
    int32_t last;
    last = flow_node_create_decoder(c, &g, -1, 0);
    last = flow_node_create_bitmap_bgra_reference(c, &g, last, ref);

    if (flow_context_has_error(c)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    if (!flow_job_execute(c, job, &g)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    // Let the bitmap last longer than the job
    if (!flow_set_owner(c, *ref, bitmap_owner)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    if (!flow_job_destroy(c, job)) {
        FLOW_error_return(c);
    }
    FLOW_free(c, bytes);
    return true;
}
Exemplo n.º 5
0
bool visual_compare_two(flow_c * c, struct flow_bitmap_bgra * a, struct flow_bitmap_bgra * b, const char * comparison_title,
                        double * out_dssim, bool save_bitmaps, bool generate_visual_diff, const char * file_,
                        const char * func_, int line_number, const char * storage_relative_to)
{

    char checksum_a[34];

    char checksum_b[34];
    // compute checksum of bitmap (two checksums, actually - one for configuration, another for bitmap bytes)
    if (!checksum_bitmap(c, a, checksum_a, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }
    if (!checksum_bitmap(c, b, checksum_b, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }

    // Compare
    if (strcmp(checksum_a, checksum_b) == 0) {
        if (memcmp(a->pixels, b->pixels, a->stride * a->h) == 0) {
            *out_dssim = 0;
            return true; // It matches!
        } else {
            // Checksum collsion
            exit(901);
        }
    }
    if (save_bitmaps) {
        // They differ
        if (!save_bitmap_to_visuals(c, a, checksum_a, "A", storage_relative_to)) {
            FLOW_error_return(c);
        }
        if (!save_bitmap_to_visuals(c, b, checksum_b, "B", storage_relative_to)) {
            FLOW_error_return(c);
        }
        // Diff the two, generate a third PNG. Also get PSNR metrics from imagemagick
        if (!diff_images(c, checksum_a, checksum_b, out_dssim, generate_visual_diff && save_bitmaps, storage_relative_to)) {
            FLOW_error_return(c);
        }
        // Dump to HTML=
        if (!append_html(c, comparison_title, checksum_a, checksum_b, storage_relative_to)) {
            FLOW_error_return(c);
        }
    }

    return false;
}
Exemplo n.º 6
0
bool flow_job_link_codecs(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{

    if (graph_ref == NULL || *graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
        FLOW_error_return(c);
    }

    struct flow_graph * g = *graph_ref;
    int32_t i;
    for (i = 0; i < g->next_node_id; i++) {
        if (g->nodes[i].type == flow_ntype_decoder || g->nodes[i].type == flow_ntype_encoder) {
            uint8_t * info_bytes = &g->info_bytes[g->nodes[i].info_byte_index];
            struct flow_nodeinfo_codec * info = (struct flow_nodeinfo_codec *)info_bytes;
            if (info->codec == NULL) {
                info->codec = flow_job_get_codec_instance(c, job, info->placeholder_id);

                if (info->codec == NULL)
                    FLOW_error_msg(c, flow_status_Graph_invalid,
                                   "No matching codec or io found for placeholder id %d (node #%d).",
                                   info->placeholder_id, i);
            }
        }
    }

    return true;
}
Exemplo n.º 7
0
bool flow_job_force_populate_dimensions(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    // TODO: would be good to verify graph is acyclic.
    if (!flow_graph_walk(c, job, graph_ref, node_visitor_dimensions, NULL, (void *)true)) {
        FLOW_error_return(c);
    }
    return true;
}
Exemplo n.º 8
0
bool flow_job_populate_dimensions_where_certain(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    // TODO: would be good to verify graph is acyclic.
    if (!flow_graph_walk_dependency_wise(c, job, graph_ref, node_visitor_dimensions, NULL, (void *)false)) {
        FLOW_error_return(c);
    }
    return true;
}
Exemplo n.º 9
0
static bool flow_job_populate_dimensions_for_node(flow_c * c, struct flow_job * job, struct flow_graph * g,
                                                  int32_t node_id, bool force_estimate)
{
    uint64_t now = flow_get_high_precision_ticks();

    if (!flow_node_populate_dimensions(c, g, node_id, force_estimate)) {
        FLOW_error_return(c);
    }
    g->nodes[node_id].ticks_elapsed += (int32_t)(flow_get_high_precision_ticks() - now);
    return true;
}
Exemplo n.º 10
0
bool get_image_dimensions(flow_c * c, uint8_t * bytes, size_t bytes_count, int32_t * width, int32_t * height)
{
    struct flow_job * job = flow_job_create(c);
    if (job == NULL) {
        FLOW_error_return(c);
    }

    struct flow_io * input = flow_io_create_from_memory(c, flow_io_mode_read_seekable, bytes, bytes_count, job, NULL);
    if (input == NULL) {
        FLOW_error_return(c);
    }
    if (!flow_job_add_io(c, job, input, 0, FLOW_INPUT)) {
    }
    struct flow_decoder_info info;
    if (!flow_job_get_decoder_info(c, job, 0, &info)) {
        FLOW_error_return(c);
    }
    *width = info.frame0_width;
    *height = info.frame0_height;
    if (!flow_job_destroy(c, job)) {
        FLOW_error_return(c);
    }
    return true;
}
Exemplo n.º 11
0
bool flow_graph_pre_optimize_flatten(flow_c * c, struct flow_graph ** graph_ref)
{
    if (*graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    bool re_walk;
    do {
        re_walk = false;
        if (!flow_graph_walk_dependency_wise(c, NULL, graph_ref, node_visitor_flatten, NULL, &re_walk)) {
            FLOW_error_return(c);
        }
    } while (re_walk);
    return true;
}
Exemplo n.º 12
0
bool flow_graph_optimize(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    if (*graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    bool re_walk;
    do {
        re_walk = false;
        if (!flow_graph_walk(c, job, graph_ref, node_visitor_optimize, NULL, &re_walk)) {
            FLOW_error_return(c);
        }
    } while (re_walk);
    return true;
}
Exemplo n.º 13
0
bool flow_job_execute_where_certain(flow_c * c, struct flow_job * job, struct flow_graph ** g)
{
    if (*g == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }

    //    //Resets and creates state tracking for this graph
    //    if (!flow_job_create_state(c,job, *g)){
    //        FLOW_error_return(c);
    //    }

    if (!flow_graph_walk_dependency_wise(c, job, g, node_visitor_execute, NULL, NULL)) {
        FLOW_error_return(c);
    }
    return true;
}
Exemplo n.º 14
0
bool visual_compare(flow_c * c, struct flow_bitmap_bgra * bitmap, const char * name, bool store_checksums,
                    size_t off_by_one_byte_differences_permitted, const char * file_, const char * func_,
                    int line_number, const char * storage_relative_to)
{

    char checksum[34];

    // compute checksum of bitmap (two checksums, actually - one for configuration, another for bitmap bytes)
    if (!checksum_bitmap(c, bitmap, checksum, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }
    // Load stored checksum
    char * stored_checksum = get_checksum_for(c, name, storage_relative_to);

    // Compare
    if (stored_checksum != NULL && strcmp(checksum, stored_checksum) == 0) {
        // Make sure the file is created for later
        if (!save_bitmap_to_visuals(c, bitmap, checksum, "trusted", storage_relative_to)) {
            FLOW_error_return(c);
        }
        return true; // It matches!
    }

    if (stored_checksum == NULL) {
        // No stored checksum for this name
        if (store_checksums) {
            if (!append_checksum(c, checksum, name, storage_relative_to)) {
                FLOW_error_return(c);
            }
            fprintf(stderr, "===============\n%s\nStoring checksum %s, since FLOW_STORE_CHECKSUMS was set.\n ", name,
                    &checksum[0]);
        } else {
            fprintf(stderr, "===============\n%s\nThere is no stored checksum for this test; #define "
                            "FLOW_STORE_CHECKSUMS and rerun to set the initial value to %s.\n ",
                    name, &checksum[0]);
        }

        fprintf(stderr, "%s:%d in function %s\n", file_, line_number, func_);
    } else {
        fprintf(stderr, "===============\n%s\nThe stored checksum [%s] differs from the current result [%s]. Open "
                        "visuals/visuals.html to comapre.\n ",
                name, stored_checksum, checksum);
        fprintf(stderr, "%s:%d in function %s\n", file_, line_number, func_);
    }

    // The hash differs
    // Save ours so we can see it
    if (!save_bitmap_to_visuals(c, bitmap, checksum, "current", storage_relative_to)) {
        FLOW_error_return(c);
    }
    if (stored_checksum == NULL && store_checksums) {
        // Don't fail the test for an non-stored checksum. We may be trying to commit several new checksums
        return true;
    }

    if (stored_checksum != NULL) {
        // Try to download "old" png from S3 using the checksums as an address.
        if (!download_by_checksum(c, stored_checksum, storage_relative_to)) {
            FLOW_error_return(c);
        }

        // First try a byte-by-byte comparison to eliminate floating-point error
        struct flow_bitmap_bgra * old;
        if (!load_image(c, stored_checksum, &old, c, storage_relative_to)) {
            FLOW_error_return(c);
        }
        size_t differences;
        size_t differences_to_print = 0; // 100
        size_t total_delta;
        if (!diff_image_pixels(c, old, bitmap, &differences, &total_delta, 0,
                               off_by_one_byte_differences_permitted + 4096)) {
            FLOW_error_return(c);
        }
        // If the difference is a handful of off-by-one rounding changes, just print the different bytes.
        if (differences < off_by_one_byte_differences_permitted && total_delta == differences) {
            if (!diff_image_pixels(c, old, bitmap, &differences, &total_delta, differences_to_print,
                                   off_by_one_byte_differences_permitted + 4096)) {
                FLOW_error_return(c);
            }
            fprintf(stderr, "\nA total of %lu bytes (of %lu) differed between the bitmaps (off by one). Less than "
                            "failure threshold of %lu\n",
                    differences, (size_t)old->stride * (size_t)old->h, off_by_one_byte_differences_permitted);
            return true;
        } else {

            double dssim;
            // Diff the two, generate a third PNG. Also get PSNR metrics from imagemagick
            if (!diff_images(c, checksum, stored_checksum, &dssim, true, storage_relative_to)) {
                FLOW_error_return(c);
            }
            fprintf(stdout, "DSSIM %.20f between %s and %s\n", dssim, stored_checksum, checksum);

            // Dump to HTML=
            if (!append_html(c, name, checksum, stored_checksum, storage_relative_to)) {
                FLOW_error_return(c);
            }
        }
        flow_bitmap_bgra_destroy(c, old);
    }

    return false;
}
Exemplo n.º 15
0
bool flow_job_execute(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
        FLOW_error_return(c);
    }
    if (!flow_job_link_codecs(c, job, graph_ref)) {
        FLOW_error_return(c);
    }

    // States for a node
    // New
    // OutboundDimensionsKnown
    // Flattened
    // Optimized
    // LockedForExecution
    // Executed
    int32_t passes = 0;
    while (!flow_job_graph_fully_executed(c, job, *graph_ref)) {
        if (passes >= job->max_calc_flatten_execute_passes) {
            FLOW_error(c, flow_status_Maximum_graph_passes_exceeded);
            return false;
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_pre_optimize_flatten(c, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_optimize(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_post_optimize_flatten(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_execute_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        passes++;

        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
    }
    if (job->next_graph_version > 0 && job->render_last_graph
        && !flow_job_render_graph_to_png(c, job, *graph_ref, job->next_graph_version - 1)) {
        FLOW_error_return(c);
    }
    return true;
}