Пример #1
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;
}
Пример #2
0
static bool append_html(flow_c * c, const char * name, const char * checksum_a, const char * checksum_b,
                       const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to, true, filename, 2048, "/visuals/visuals.html")) {
        FLOW_add_to_callstack(c);
        return false;
    }
    static bool first_write = true;

    FILE * fp = fopen(filename, first_write ? "w" : "a");
    if (fp == NULL) {
        FLOW_error(c, flow_status_IO_error);
        return false;
    }
    if (first_write) {
        // Write the header here
    }
    if (checksum_b == NULL) {
        fprintf(fp, "<h1>%s</h2>\n<img class=\"current\" src=\"%s.png\"/>", name, checksum_a);
    } else {
        fprintf(fp, "<h1>%s</h2>\n<img class=\"current\" src=\"%s.png\"/><img class=\"correct\" src=\"%s.png\"/><img "
                    "class=\"diff\" src=\"compare_%s_vs_%s.png\"/>",
                name, checksum_a, checksum_b, checksum_a, checksum_b);
    }

    fclose(fp);
    first_write = false;
    return true;
}
Пример #3
0
static bool save_bitmap_to_visuals(flow_c * c, struct flow_bitmap_bgra * bitmap, char * checksum, char * name)
{
    char filename[2048];
    if (!create_relative_path(c, true, filename, 2048, "/visuals/%s.png", checksum)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    if (access(filename, F_OK) != -1) {
        return true; // Already exists!
    }

    if (!write_frame_to_disk(c, &filename[0], bitmap)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    fprintf(stderr, "%s (%s)\n", &filename[0], name);
    return true;
}
Пример #4
0
static bool diff_images(flow_c * c, char * checksum_a, char * checksum_b, double * out_dssim, bool generate_visual_diff,
                       const char * storage_relative_to)
{
    char filename_a[2048];
    if (!create_path_from_relative(c, storage_relative_to, false, filename_a, 2048, "/visuals/%s.png", checksum_a)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    char filename_b[2048];
    if (!create_path_from_relative(c, storage_relative_to, false, filename_b, 2048, "/visuals/%s.png", checksum_b)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    char filename_c[2048];
    if (!create_path_from_relative(c, storage_relative_to, false, filename_c, 2048, "/visuals/compare_%s_vs_%s.png", checksum_a, checksum_b)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    char magick_command[4096];
    flow_snprintf(magick_command, 4096, "dssim %s %s", filename_b, filename_a);
    *out_dssim = get_dssim_from_command(c, magick_command);
    if (*out_dssim > 10 || *out_dssim < 0) {
        fprintf(stderr, "Failed to execute: %s", magick_command);
        *out_dssim = 2.23456;
        // FLOW_error(c, flow_status_IO_error);
    };
    if (generate_visual_diff) {
        fprintf(stderr, "%s\n", &filename_c[0]);

        if (access(filename_c, F_OK) != -1) {
            return true; // Already exists!
        }
        flow_snprintf(magick_command, 4096, "composite %s %s -compose difference %s", filename_a, filename_b,
                      filename_c);

        int result = system(magick_command);
        if (result != 0) {
            fprintf(stderr, "unhappy imagemagick\n");
        }
    }
    return true;
}
Пример #5
0
static bool download_by_checksum(flow_c * c, char * checksum, const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to, true, filename, 2048, "/visuals/%s.png", checksum)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    fprintf(stderr, "%s (trusted)\n", &filename[0]);
    if (access(filename, F_OK) != -1) {
        return true; // Already exists!
    }
    char url[2048];
    flow_snprintf(url, 2048, "http://s3-us-west-2.amazonaws.com/imageflow-resources/visual_test_checksums/%s.png",
                  checksum); // TODO: fix actual URL
    if (!fetch_image(url, filename)) {
        FLOW_add_to_callstack(c);
        return false;
    }
    return true;
}
Пример #6
0
bool load_image(flow_c * c, char * checksum, struct flow_bitmap_bgra ** ref, void * bitmap_owner,
                const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to,  false, filename, 2048, "/visuals/%s.png", checksum)) {
        FLOW_add_to_callstack(c);
        return false;
    }

    // load PNG
    if (!flow_bitmap_bgra_load_png(c, ref, filename)) {
        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;
    }
    return true;
}
Пример #7
0
struct flow_interpolation_details * flow_interpolation_details_create_custom(flow_c * context, double window,
                                                                             double blur,
                                                                             flow_detailed_interpolation_method filter)
{
    struct flow_interpolation_details * d = flow_interpolation_details_create(context);
    if (d != NULL) {
        d->blur = blur;
        d->filter = filter;
        d->window = window;
    } else {
        FLOW_add_to_callstack(context);
    }
    return d;
}
Пример #8
0
struct flow_interpolation_details * flow_interpolation_details_create_bicubic_custom(flow_c * context, double window,
                                                                                     double blur, double B, double C)
{
    struct flow_interpolation_details * d = flow_interpolation_details_create(context);
    if (d != NULL) {
        d->blur = blur;
        derive_cubic_coefficients(B, C, d);
        d->filter = filter_flex_cubic;
        d->window = window;
    } else {
        FLOW_add_to_callstack(context);
    }
    return d;
}
Пример #9
0
static char * get_checksum_for(flow_c * c, const char * name, const char * storage_relative_to)
{
    struct named_checksum * checksums = NULL;
    size_t checksum_count = 0;

    if (!load_checksums(c, &checksums, &checksum_count, storage_relative_to)) {
        FLOW_add_to_callstack(c);
        return NULL;
    }
    for (size_t i = 0; i < checksum_count; i++) {
        if (strcmp(checksums[i].name, name) == 0) {
            return checksums[i].checksum;
        }
    }
    return NULL;
}
Пример #10
0
bool append_checksum(flow_c * c, char checksum[34], const char * name, const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to, true, filename, 2048, "/visuals/checksums.list")) {
        FLOW_add_to_callstack(c);
        return false;
    }
    FILE * fp = fopen(filename, "a");
    if (fp == NULL) {
        FLOW_error(c, flow_status_IO_error);
        return false;
    }
    fprintf(fp, "%s\n%s\n", name, &checksum[0]);
    fclose(fp);
    return true;
}
Пример #11
0
static bool load_checksums(flow_c * c, struct named_checksum ** checksums, size_t * checksum_count,
                          const char * storage_relative_to)
{
    static struct named_checksum * list = NULL;
    static size_t list_size = 0;

    if (list == NULL) {
        char filename[2048];
        if (!create_path_from_relative(c,storage_relative_to, false, filename, 2048, "/visuals/checksums.list")) {
            FLOW_add_to_callstack(c);
            return false;
        }
        //fprintf(stderr, "Using checkums from %s.", filename);
        FILE * fp;
        char * line_a = NULL;
        size_t len_a = 0;
        int64_t read_a;
        char * line_b = NULL;
        size_t len_b = 0;
        int64_t read_b;

        fp = fopen(filename, "r");
        if (fp == NULL) {
            FLOW_error(c, flow_status_IO_error);
            return false;
        }

        list_size = 200;
        list = (struct named_checksum *)calloc(list_size, sizeof(struct named_checksum));

        size_t index = 0;
        while (true) {
            // Read lines in pairs
            read_a = flow_getline(&line_a, &len_a, fp);
            if (read_a == -1) {
                break;
            }
            read_b = flow_getline(&line_b, &len_b, fp);
            if (read_b == -1) {
                free(line_a);
                break;
            }
            // Drop newlines if present
            if (line_a[read_a - 1] == '\n') {
                line_a[read_a - 1] = '\0';
            }
            if (line_b[read_b - 1] == '\n') {
                line_b[read_b - 1] = '\0';
            }
            // Save
            list[index].name = line_a;
            list[index].checksum = line_b;
            line_a = NULL;
            line_b = NULL;
            index++;
            if (index >= list_size) {
                FLOW_error_msg(c, flow_status_IO_error,
                               "Could not read in entire checksum file. Please increase list_size above %ul.",
                               list_size);
                fclose(fp);
                return false;
            }
        }
        list_size = index;
        fclose(fp);
    }
    *checksum_count = list_size;
    *checksums = list;

    return true;
}
Пример #12
0
struct flow_interpolation_line_contributions *
flow_interpolation_line_contributions_create(flow_c * context, const uint32_t output_line_size,
                                             const uint32_t input_line_size,
                                             const struct flow_interpolation_details * details)
{
    const double sharpen_ratio = flow_interpolation_details_percent_negative_weight(details);
    const double desired_sharpen_ratio = details->sharpen_percent_goal / 100.0;

    const double scale_factor = (double)output_line_size / (double)input_line_size;
    const double downscale_factor = fmin(1.0, scale_factor);
    const double half_source_window = (details->window + 0.5) / downscale_factor;

    const uint32_t allocated_window_size = (int)ceil(2 * (half_source_window - TONY)) + 1;
    uint32_t u, ix;
    struct flow_interpolation_line_contributions * res
        = LineContributions_alloc(context, output_line_size, allocated_window_size);
    if (res == NULL) {
        FLOW_add_to_callstack(context);
        return NULL;
    }
    double negative_area = 0;
    double positive_area = 0;

    for (u = 0; u < output_line_size; u++) {
        const double center_src_pixel = ((double)u + 0.5) / scale_factor - 0.5;

        const int left_edge = (int)floor(center_src_pixel) - ((allocated_window_size - 1) / 2);
        const int right_edge = left_edge + allocated_window_size - 1;

        const uint32_t left_src_pixel = (uint32_t)int_max(0, left_edge);
        const uint32_t right_src_pixel = (uint32_t)int_min(right_edge, (int)input_line_size - 1);

        double total_weight = 0.0;
        double total_negative_weight = 0.0;

        const uint32_t source_pixel_count = right_src_pixel - left_src_pixel + 1;

        if (source_pixel_count > allocated_window_size) {
            flow_interpolation_line_contributions_destroy(context, res);
            FLOW_error(context, flow_status_Invalid_internal_state);
            return NULL;
        }

        res->ContribRow[u].Left = left_src_pixel;
        res->ContribRow[u].Right = right_src_pixel;

        float * weights = res->ContribRow[u].Weights;

        // commented: additional weight for edges (doesn't seem to be too effective)
        // for (ix = left_edge; ix <= right_edge; ix++) {
        for (ix = left_src_pixel; ix <= right_src_pixel; ix++) {
            int tx = ix - left_src_pixel;
            // int tx = min(max(ix, left_src_pixel), right_src_pixel) - left_src_pixel;
            double add = (*details->filter)(details, downscale_factor *((double)ix - center_src_pixel));
            if (fabs(add) <= 0.00000002){
                add = 0.0;
                // Weights below a certain threshold make consistent x-plat
                // integration test results impossible. pos/neg zero, etc.
                // They should be rounded down to zero at the threshold at which results are consistent.
            }
            weights[tx] = (float)add;
            total_weight += add;
            total_negative_weight -= fmin(0,add);
        }

        float neg_factor, pos_factor;
        if (total_weight <= 0 || desired_sharpen_ratio > sharpen_ratio){
            float total_positive_weight = total_weight + total_negative_weight;
            float target_negative_weight = desired_sharpen_ratio * total_positive_weight;
            pos_factor = 1;
            neg_factor = target_negative_weight / total_negative_weight;
        }else{
            neg_factor = pos_factor = (float)(1.0f / total_weight);
        }
        for (ix = 0; ix < source_pixel_count; ix++) {
            if (weights[ix] < 0) {
                weights[ix] *= neg_factor;
                negative_area -= weights[ix];
            } else {
                weights[ix] *= pos_factor;
                positive_area += weights[ix];
            }
        }

        //Shrink to improve perf & result consistency
        int32_t iix;
        //Shrink region from the right
        for (iix = source_pixel_count - 1; iix >= 0; iix--){
            if (weights[iix] != 0) break;
            res->ContribRow[u].Right--;
        }
        //Shrink region from the left
        for (iix = 0; iix < (int32_t)source_pixel_count; iix++){
            if (weights[0] != 0) break;
            res->ContribRow[u].Weights++;
            weights++;
            res->ContribRow[u].Left++;
        }
    }
    res->percent_negative = negative_area / positive_area;
    return res;
}