From 1773494c339e02b13ce800bcc2dcd7214181e000 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Wed, 26 Jan 2022 03:15:14 +0100 Subject: [PATCH] babl: measure performance of LUTs and only replace if faster With this also enabling replacement of conversions where spaces do not differ. --- babl/babl-fish-path.c | 1640 +++++++++++++++++++++-------------------- 1 file changed, 859 insertions(+), 781 deletions(-) diff --git a/babl/babl-fish-path.c b/babl/babl-fish-path.c index e49dcfd..f254452 100644 --- a/babl/babl-fish-path.c +++ b/babl/babl-fish-path.c @@ -32,576 +32,909 @@ #define MIN(a, b) (((a) > (b)) ? (b) : (a)) #endif -#define MAX_BUFFER_SIZE 512 -#define ITERATIONS 4 -int babl_in_fish_path = 0; +typedef struct GcContext { + long time; +} GcContext; -typedef struct _FishPathInstrumentation +static int gc_fishes (Babl *babl, void *userdata) { - const Babl *fmt_rgba_double; - int num_test_pixels; - void *source; - void *destination; - void *ref_destination; - double *destination_rgba_double; - double *ref_destination_rgba_double; - const Babl *fish_rgba_to_source; - const Babl *fish_reference; - const Babl *fish_destination_to_rgba; - double reference_cost; - int init_instrumentation_done; -} FishPathInstrumentation; - -typedef struct PathContext { - Babl *fish_path; - Babl *to_format; - BablList *current_path; -} PathContext; - -static void -init_path_instrumentation (FishPathInstrumentation *fpi, - Babl *fmt_source, - Babl *fmt_destination); - -static void -destroy_path_instrumentation (FishPathInstrumentation *fpi); - -static void -get_path_instrumentation (FishPathInstrumentation *fpi, - BablList *path, - double *path_cost, - double *ref_cost, - double *path_error); - - -static inline void -process_conversion_path (BablList *path, - const void *source_buffer, - int source_bpp, - void *destination_buffer, - int dest_bpp, - long n); - + GcContext *context = userdata; + if (babl->class_type == BABL_FISH_PATH) + { + if (babl->fish_path.u8_lut) + { + if (context->time - babl->fish_path.last_lut_use > + 1000 * 1000 * 60 * 5) + { + void *lut =babl->fish_path.u8_lut; + BABL(babl)->fish_path.u8_lut = NULL; + free (lut); +#if 0 + fprintf (stderr, "freeing LUT %s to %s unused for >5 minutes\n", + babl_get_name (babl->conversion.source), + babl_get_name (babl->conversion.destination)); +#endif + } + } + } + return 0; +} + static void -get_conversion_path (PathContext *pc, - Babl *current_format, - int current_length, - int max_length, - double legal_error); - -char * -_babl_fish_create_name (char *buf, - const Babl *source, - const Babl *destination, - int is_reference); - - -static int max_path_length (void); - -static int debug_conversions = 0; -int _babl_instrument = 0; - -double -_babl_legal_error (void) +babl_gc_fishes (void) { - static double error = 0.0; - const char *env; - - if (error != 0.0) - return error; + GcContext context; + context.time = babl_ticks (); + babl_fish_class_for_each (gc_fishes, &context); + //malloc_trim (0); + // is responsibility of higher layers +} - env = getenv ("BABL_TOLERANCE"); - if (env && env[0] != '\0') - error = babl_parse_double (env); - else - error = BABL_TOLERANCE; +#define BABL_LIKELY(x) __builtin_expect(!!(x), 1) +#define BABL_UNLIKELY(x) __builtin_expect(!!(x), 0) - env = getenv ("BABL_DEBUG_CONVERSIONS"); - if (env && env[0] != '\0') - debug_conversions = 1; - else - debug_conversions = 0; +static long timings[256] = {0,}; - env = getenv ("BABL_INSTRUMENT"); - if (env && env[0] != '\0') - _babl_instrument = 1; - else - _babl_instrument = 0; +static inline void _do_lut (uint32_t *lut, + int source_bpp, + int dest_bpp, + void *__restrict__ source, + void *__restrict__ destination, + long n) +{ + if (source_bpp == 4 && dest_bpp == 16) + { + uint32_t *src = (uint32_t*)source; + uint32_t *dst = (uint32_t*)destination; + while (n--) + { + uint32_t col = *src++; + uint32_t lut_offset = col & 0xffffff; + float alpha = (col>>24)/255.0; - return error; + *dst++ = lut[lut_offset*4+0]; + *dst++ = lut[lut_offset*4+1]; + *dst++ = lut[lut_offset*4+2]; + ((float*)(dst))[0] = alpha; + dst++; + } + return 1; + } + else if (source_bpp == 4 && dest_bpp == 4) + { + uint32_t *src = (uint32_t*)source; + uint32_t *dst = (uint32_t*)destination; + while (n--) + { + uint32_t col = *src++; + *dst = col & 0xff000000; + *dst |= lut[col & 0xffffff]; + dst++; + } + return 1; + } + else if (source_bpp == 2 && dest_bpp == 4) + { + uint16_t *src = (uint16_t*)source; + uint32_t *dst = (uint32_t*)destination; + while (n--) + { + *dst = lut[*src++]; + dst++; + } + return 1; + } + else if (source_bpp == 1 && dest_bpp == 4) + { + uint8_t *src = (uint8_t*)source; + uint32_t *dst = (uint32_t*)destination; + while (n--) + { + *dst = lut[*src++]; + dst++; + } + return 1; + } + else if (source_bpp == 3 && dest_bpp == 3) + { + uint8_t *src = (uint8_t*)source; + uint8_t *dst = (uint8_t*)destination; + while (n--) + { + uint32_t col = src[0]*256*256+src[1]*256+src[2]; + uint32_t val = lut[col]; + dst[2]=(val >> 16) & 0xff; + dst[1]=(val >> 8) & 0xff; + dst[0]=val & 0xff; + dst+=3; + src+=3; + } + return 1; + } + else if (source_bpp == 3 && dest_bpp == 4) + { + uint8_t *src = (uint8_t*)source; + uint32_t *dst = (uint32_t*)destination; + while (n--) + { + uint32_t col = src[0]*256*256+src[1]*256+src[2]; + *dst = lut[col]; + dst++; + src+=3; + } + return 1; + } } -static int -max_path_length (void) +void do_lut (uint32_t *lut, + int source_bpp, + int dest_bpp, + void *__restrict__ source, + void *__restrict__ dest, + long count) { - static int max_length = 0; - const char *env; - - if (max_length != 0) - return max_length; - - env = getenv ("BABL_PATH_LENGTH"); - if (env) - max_length = atoi (env); - else - max_length = 3; /* reducing this number makes finding short fishes much - faster - even if we lose out on some of the fast - bigger fish, the fishes we can get with a max_length of 3 - is actually 5, since we deepen the search to that - depth if none are found within two steps in the - initial search. - */ - if (max_length > BABL_HARD_MAX_PATH_LENGTH) - max_length = BABL_HARD_MAX_PATH_LENGTH; - else if (max_length <= 0) - max_length = 1; - return max_length; + _do_lut (lut, source_bpp, dest_bpp, source, dest, count); } -int -_babl_max_path_len (void) +static inline long lut_timing_for (int source_bpp, int dest_bpp) { - return max_path_length (); + return timings[source_bpp * 16 + dest_bpp]; } -static int -bad_idea (const Babl *from, const Babl *to, const Babl *format) +static void measure_timings(void) { - if (babl_format_has_alpha (from) && - babl_format_has_alpha (to) && - !babl_format_has_alpha (format)) - { - return 1; - } - if (from->format.components > format->format.components && - to->format.components > format->format.components) - { - return 1; - } - if (from->format.type[0]->bits > format->format.type[0]->bits && - to->format.type[0]->bits > format->format.type[0]->bits) - { - /* XXX: perhaps we especially avoid going to half-float, when - * going between u16 formats as well? */ - return 1; - } - - return 0; + int num_pixels = babl_get_num_path_test_pixels () * 1000; + int pairs[][2]={{4,4},{3,4},{3,3},{2,4},{1,4},{4,16}}; + uint32_t *lut = malloc (256 * 256 * 256 * 16); + uint32_t *src = malloc (num_pixels * 16); + uint32_t *dst = malloc (num_pixels * 16); + fprintf (stderr, "measuring lut timings \n"); + for (int p = 0; p < sizeof (pairs)/sizeof(pairs[0]);p++) + { + int source_bpp = pairs[p][0]; + int dest_bpp = pairs[p][1]; + long start,end; + start = babl_ticks (); + _do_lut (lut, source_bpp, dest_bpp, src, dst, num_pixels); + + end = babl_ticks (); + + timings[source_bpp * 16 + dest_bpp] = (end-start)/1000; + fprintf (stderr, "%i %i: %i\n", source_bpp, dest_bpp, + timings[source_bpp * 16 + dest_bpp] + ); + } + free (lut); + free (src); + free (dst); } +static inline void +process_conversion_path (BablList *path, + const void *source_buffer, + int source_bpp, + void *destination_buffer, + int dest_bpp, + long n); -/* The task of BablFishPath construction is to compute - * the shortest path in a graph where formats are the vertices - * and conversions are the edges. However, there is an additional - * constraint to the shortest path, that limits conversion error - * introduced by such a path to be less than BABL_TOLERANCE. This - * prohibits usage of any reasonable shortest path construction - * algorithm such as Dijkstra's algorithm. The shortest path is - * constructed by enumerating all available paths that are less - * than BABL_PATH_LENGTH long, computing their costs and - * conversion errors and backtracking. The backtracking is - * implemented by recursive function get_conversion_path (). - */ - -static void -get_conversion_path (PathContext *pc, - Babl *current_format, - int current_length, - int max_length, - double legal_error) +static int babl_fish_lut_process_maybe (const Babl *babl, + const char *source, + const char *destination, + long n, + void *data) { - if (current_length > max_length) - { - /* We have reached the maximum recursion - * depth, let's bail out */ - return; - } - else if ((current_length > 0) && (current_format == pc->to_format)) - { - /* We have found a candidate path, let's - * see about it's properties */ - double path_cost = 0.0; - double ref_cost = 0.0; - double path_error = 1.0; -#if 1 - int i; - for (i = 0; i < babl_list_size (pc->current_path); i++) - { - path_error *= (1.0 + babl_conversion_error ((BablConversion *) pc->current_path->items[i])); - } - - if (path_error - 1.0 <= legal_error ) - /* check this before the more accurate measurement of error - - to bail earlier, this also leads to a stricter - discarding of bad fast paths */ -#endif - { - FishPathInstrumentation fpi; - memset (&fpi, 0, sizeof (fpi)); + int source_bpp = babl->fish_path.source_bpp; + int dest_bpp = babl->fish_path.dest_bpp; + uint32_t *lut = (uint32_t*)babl->fish_path.u8_lut; + BABL(babl)->fish.pixels += n; - fpi.source = (Babl*) babl_list_get_first (pc->current_path)->conversion.source; - fpi.destination = pc->to_format; - get_path_instrumentation (&fpi, pc->current_path, &path_cost, &ref_cost, &path_error); - if(debug_conversions && current_length == 1) - fprintf (stderr, "%s error:%f cost:%f \n", - babl_get_name (pc->current_path->items[0]), path_error, path_cost); + if (BABL_UNLIKELY(!lut && babl->fish.pixels >= 128 * 256)) + { +#if 0 + fprintf (stderr, "building LUT for %s to %s\n", + babl_get_name (babl->conversion.source), + babl_get_name (babl->conversion.destination)); +#endif + if (source_bpp ==4 && dest_bpp == 4) + { + lut = malloc (256 * 256 * 256 * 4); + for (int o = 0; o < 256 * 256 * 256; o++) + lut[o] = o; + process_conversion_path (babl->fish_path.conversion_list, + lut, 4, + lut, 4, + 256*256*256); + for (int o = 0; o < 256 * 256 * 256; o++) + lut[o] = lut[o] & 0x00ffffff; + } + else if (source_bpp == 4 && dest_bpp == 16) + { + uint32_t *temp_lut = malloc (256 * 256 * 256 * 4); + lut = malloc (256 * 256 * 256 * 16); + for (int o = 0; o < 256 * 256 * 256; o++) + temp_lut[o] = o; + process_conversion_path (babl->fish_path.conversion_list, + temp_lut, 4, + lut, 16, + 256*256*256); + free (temp_lut); + } + else if (source_bpp == 3 && dest_bpp == 3) + { + lut = malloc (256 * 256 * 256 * 4); + uint8_t *temp_lut = malloc (256 * 256 * 256 * 3); + uint8_t *temp_lut2 = malloc (256 * 256 * 256 * 3); + int o = 0; + for (int r = 0; r < 256; r++) + for (int g = 0; g < 256; g++) + for (int b = 0; b < 256; b++, o++) + { + temp_lut[o*3+0]=r; + temp_lut[o*3+1]=g; + temp_lut[o*3+2]=b; + } + process_conversion_path (babl->fish_path.conversion_list, + temp_lut, 3, + temp_lut2, 3, + 256*256*256); + babl_process (babl_fish (babl_format ("R'G'B' u8"), babl_format ("R'G'B'A u8")), + temp_lut2, lut, 256*256*256); + for (int o = 0; o < 256 * 256 * 256; o++) + lut[o] = lut[o] & 0x00ffffff; + free (temp_lut); + free (temp_lut2); + } + else if (source_bpp == 3 && dest_bpp == 4) + { + lut = malloc (256 * 256 * 256 * 4); + uint8_t *temp_lut = malloc (256 * 256 * 256 * 3); + int o = 0; + for (int r = 0; r < 256; r++) + for (int g = 0; g < 256; g++) + for (int b = 0; b < 256; b++, o++) + { + temp_lut[o*3+0]=r; + temp_lut[o*3+1]=g; + temp_lut[o*3+2]=b; + } + process_conversion_path (babl->fish_path.conversion_list, + temp_lut, 3, + lut, 4, + 256*256*256); + for (int o = 0; o < 256 * 256 * 256; o++) + lut[o] = lut[o] & 0x00ffffff; + free (temp_lut); + } + else if (source_bpp == 2 && dest_bpp == 4) + { + lut = malloc (256 * 256 * 4); + uint16_t *temp_lut = malloc (256 * 256 * 2); + for (int o = 0; o < 256*256; o++) + { + temp_lut[o]=o; + } + process_conversion_path (babl->fish_path.conversion_list, + temp_lut, 2, + lut, 4, + 256*256); + for (int o = 0; o < 256 * 256; o++) + lut[o] = lut[o] & 0x00ffffff; + free (temp_lut); + } + else if (source_bpp == 1 && dest_bpp == 4) + { + lut = malloc (256 * 4); + uint8_t *temp_lut = malloc (256); + for (int o = 0; o < 256; o++) + { + temp_lut[o]=o; + } + process_conversion_path (babl->fish_path.conversion_list, + temp_lut, 1, + lut, 4, + 256); + for (int o = 0; o < 256; o++) + lut[o] = lut[o] & 0x00ffffff; + free (temp_lut); + } - if ((path_cost < ref_cost) && /* do not use paths that took longer to compute than reference */ - (path_cost < pc->fish_path->fish_path.cost) && // best thus far - (path_error <= legal_error ) // within tolerance - ) - { - /* We have found the best path so far, - * let's copy it into our new fish */ - pc->fish_path->fish_path.cost = path_cost; - pc->fish_path->fish.error = path_error; - babl_list_copy (pc->current_path, - pc->fish_path->fish_path.conversion_list); - } + if (babl->fish_path.u8_lut == NULL) + { + (BABL(babl)->fish_path.u8_lut) = lut; + // XXX need memory barrier? + if ((BABL(babl)->fish_path.u8_lut) != lut) + { + free (lut); + lut = babl->fish_path.u8_lut; + } + } + else + { + free (lut); + lut = babl->fish_path.u8_lut; + } + } + if (lut) + { + do_lut (lut, source_bpp, dest_bpp, source, destination, n); + BABL(babl)->fish_path.last_lut_use = babl_ticks (); + } + return 0; +} - destroy_path_instrumentation (&fpi); - } - } - else - { - /* - * we have to search deeper... - */ - BablList *list; - int i; - list = current_format->format.from_list; - if (list) - { - /* Mark the current format in conversion path as visited */ - current_format->format.visited = 1; - /* Iterate through unvisited formats from the current format ...*/ - for (i = 0; i < babl_list_size (list); i++) - { - Babl *next_conversion = BABL (list->items[i]); - Babl *next_format = BABL (next_conversion->conversion.destination); - if (!next_format->format.visited && !bad_idea (current_format, pc->to_format, next_format)) - { - /* next_format is not in the current path, we can pay a visit */ - babl_list_insert_last (pc->current_path, next_conversion); - get_conversion_path (pc, next_format, current_length + 1, max_length, legal_error); - babl_list_remove_last (pc->current_path); - } - } +#define MAX_BUFFER_SIZE 512 - /* Remove the current format from current path */ - current_format->format.visited = 0; - } - } -} +int babl_in_fish_path = 0; -char * -_babl_fish_create_name (char *buf, - const Babl *source, - const Babl *destination, - int is_reference) +typedef struct _FishPathInstrumentation { - /* fish names are intentionally kept short */ - snprintf (buf, BABL_MAX_NAME_LEN, "%s %p %p %i", "", - source, destination, is_reference); - return buf; -} - -int -_babl_fish_path_destroy (void *data); + const Babl *fmt_rgba_double; + int num_test_pixels; + void *source; + void *destination; + void *ref_destination; + double *destination_rgba_double; + double *ref_destination_rgba_double; + const Babl *fish_rgba_to_source; + const Babl *fish_reference; + const Babl *fish_destination_to_rgba; + double reference_cost; + int init_instrumentation_done; +} FishPathInstrumentation; -int -_babl_fish_path_destroy (void *data) +typedef struct PathContext { + Babl *fish_path; + Babl *to_format; + BablList *current_path; +} PathContext; + +static void +init_path_instrumentation (FishPathInstrumentation *fpi, + Babl *fmt_source, + Babl *fmt_destination); + +static void +destroy_path_instrumentation (FishPathInstrumentation *fpi); + +static void +get_path_instrumentation (FishPathInstrumentation *fpi, + BablList *path, + double *path_cost, + double *ref_cost, + double *path_error); + + +static inline void +process_conversion_path (BablList *path, + const void *source_buffer, + int source_bpp, + void *destination_buffer, + int dest_bpp, + long n); + +static void +get_conversion_path (PathContext *pc, + Babl *current_format, + int current_length, + int max_length, + double legal_error); + +char * +_babl_fish_create_name (char *buf, + const Babl *source, + const Babl *destination, + int is_reference); + + +static int max_path_length (void); + +static int debug_conversions = 0; +int _babl_instrument = 0; + +double +_babl_legal_error (void) { - Babl *babl=data; - if (babl->fish_path.u8_lut) - free (babl->fish_path.u8_lut); - babl->fish_path.u8_lut = NULL; - if (babl->fish_path.conversion_list) - babl_free (babl->fish_path.conversion_list); - babl->fish_path.conversion_list = NULL; - return 0; + static double error = 0.0; + const char *env; + + if (error != 0.0) + return error; + + env = getenv ("BABL_TOLERANCE"); + if (env && env[0] != '\0') + error = babl_parse_double (env); + else + error = BABL_TOLERANCE; + + env = getenv ("BABL_DEBUG_CONVERSIONS"); + if (env && env[0] != '\0') + debug_conversions = 1; + else + debug_conversions = 0; + + env = getenv ("BABL_INSTRUMENT"); + if (env && env[0] != '\0') + _babl_instrument = 1; + else + _babl_instrument = 0; + + return error; } static int -show_item (Babl *babl, - void *user_data) +max_path_length (void) { - BablConversion *conv = (void *)babl; + static int max_length = 0; + const char *env; - if (conv->destination->class_type == BABL_FORMAT) - { - fprintf (stderr, "%s : %.12f\n", babl_get_name (babl), babl_conversion_error(conv)); - } + if (max_length != 0) + return max_length; - return 0; + env = getenv ("BABL_PATH_LENGTH"); + if (env) + max_length = atoi (env); + else + max_length = 3; /* reducing this number makes finding short fishes much + faster - even if we lose out on some of the fast + bigger fish, the fishes we can get with a max_length of 3 + is actually 5, since we deepen the search to that + depth if none are found within two steps in the + initial search. + */ + if (max_length > BABL_HARD_MAX_PATH_LENGTH) + max_length = BABL_HARD_MAX_PATH_LENGTH; + else if (max_length <= 0) + max_length = 1; + return max_length; } -static int -alias_conversion (Babl *babl, - void *user_data) +int +_babl_max_path_len (void) { - const Babl *sRGB = babl_space ("sRGB"); - BablConversion *conv = (void *)babl; - BablSpace *space = user_data; + return max_path_length (); +} - if ((conv->source->class_type == BABL_FORMAT) && - (conv->destination->class_type == BABL_FORMAT) && - (!babl_format_is_palette (conv->source)) && - (!babl_format_is_palette (conv->destination))) +static int +bad_idea (const Babl *from, const Babl *to, const Babl *format) +{ + if (babl_format_has_alpha (from) && + babl_format_has_alpha (to) && + !babl_format_has_alpha (format)) { - if ((conv->source->format.space == sRGB) && - (conv->destination->format.space == sRGB)) - { - switch (conv->instance.class_type) - { - case BABL_CONVERSION_LINEAR: - babl_conversion_new ( - babl_format_with_space ( - (void*)conv->source->instance.name, (void*)space), - babl_format_with_space ( - (void*)conv->destination->instance.name, (void*)space), - "linear", conv->function.linear, - "data", conv->data, - NULL); - break; - case BABL_CONVERSION_PLANAR: - babl_conversion_new ( - babl_format_with_space ( - (void*)conv->source->instance.name, (void*)space), - babl_format_with_space ( - (void*)conv->destination->instance.name, (void*)space), - "planar", conv->function.planar, - "data", conv->data, - NULL); - break; - case BABL_CONVERSION_PLANE: - babl_conversion_new ( - babl_format_with_space ( - (void*)conv->source->instance.name, (void*)space), - babl_format_with_space ( - (void*)conv->destination->instance.name, (void*)space), - "plane", conv->function.plane, - "data", conv->data, - NULL); - break; - default: - break; - } - } + return 1; } - else - if ((conv->source->class_type == BABL_MODEL) && - (conv->destination->class_type == BABL_MODEL)) + if (from->format.components > format->format.components && + to->format.components > format->format.components) { - if ((conv->source->model.space == sRGB) && - (conv->destination->model.space == sRGB)) - { - switch (conv->instance.class_type) - { - case BABL_CONVERSION_LINEAR: - babl_conversion_new ( - babl_remodel_with_space ( - (void*)conv->source, (void*)space), - babl_remodel_with_space ( - (void*)conv->destination, (void*)space), - "linear", conv->function.linear, - "data", conv->data, - NULL); - break; - case BABL_CONVERSION_PLANAR: - babl_conversion_new ( - babl_remodel_with_space ( - (void*)conv->source, (void*)space), - babl_remodel_with_space ( - (void*)conv->destination, (void*)space), - "planar", conv->function.planar, - "data", conv->data, - NULL); - break; - case BABL_CONVERSION_PLANE: - babl_conversion_new ( - babl_remodel_with_space ( - (void*)conv->source, (void*)space), - babl_remodel_with_space ( - (void*)conv->destination, (void*)space), - "plane", conv->function.plane, - "data", conv->data, - NULL); - break; - default: - break; - } - } + return 1; } - else - if ((conv->source->class_type == BABL_TYPE) && - (conv->destination->class_type == BABL_TYPE)) + if (from->format.type[0]->bits > format->format.type[0]->bits && + to->format.type[0]->bits > format->format.type[0]->bits) { + /* XXX: perhaps we especially avoid going to half-float, when + * going between u16 formats as well? */ + return 1; } + return 0; } -void -_babl_fish_prepare_bpp (Babl *babl) -{ - const Babl *babl_source = babl->fish.source; - const Babl *babl_dest = babl->fish.destination; - switch (babl_source->instance.class_type) - { - case BABL_FORMAT: - babl->fish_path.source_bpp = babl_source->format.bytes_per_pixel; - break; - case BABL_TYPE: - babl->fish_path.source_bpp = babl_source->type.bits / 8; - break; - default: - babl_log ("=eeek{%i}\n", - babl_source->instance.class_type - BABL_MAGIC); - } - - switch (babl_dest->instance.class_type) - { - case BABL_FORMAT: - babl->fish_path.dest_bpp = babl_dest->format.bytes_per_pixel; - break; - case BABL_TYPE: - babl->fish_path.dest_bpp = babl_dest->type.bits / 8; - break; - default: - babl_log ("-eeek{%i}\n", babl_dest->instance.class_type - BABL_MAGIC); - } -} +/* The task of BablFishPath construction is to compute + * the shortest path in a graph where formats are the vertices + * and conversions are the edges. However, there is an additional + * constraint to the shortest path, that limits conversion error + * introduced by such a path to be less than BABL_TOLERANCE. This + * prohibits usage of any reasonable shortest path construction + * algorithm such as Dijkstra's algorithm. The shortest path is + * constructed by enumerating all available paths that are less + * than BABL_PATH_LENGTH long, computing their costs and + * conversion errors and backtracking. The backtracking is + * implemented by recursive function get_conversion_path (). + */ -void -_babl_fish_missing_fast_path_warning (const Babl *source, - const Babl *destination) +static void +get_conversion_path (PathContext *pc, + Babl *current_format, + int current_length, + int max_length, + double legal_error) { -#ifndef BABL_UNSTABLE - if (debug_conversions) -#endif - { - static int warnings = 0; - - if (_babl_legal_error() <= 0.0000000001) + if (current_length > max_length) + { + /* We have reached the maximum recursion + * depth, let's bail out */ return; + } + else if ((current_length > 0) && (current_format == pc->to_format)) + { + /* We have found a candidate path, let's + * see about it's properties */ + double path_cost = 0.0; + double ref_cost = 0.0; + double path_error = 1.0; +#if 1 + int i; + for (i = 0; i < babl_list_size (pc->current_path); i++) + { + path_error *= (1.0 + babl_conversion_error ((BablConversion *) pc->current_path->items[i])); + } - if (warnings++ == 0) - fprintf (stderr, -"Missing fast-path babl conversion detected, Implementing missing babl fast paths\n" -"accelerates GEGL, GIMP and other software using babl, warnings are printed on\n" -"first occurance of formats used where a conversion has to be synthesized\n" -"programmatically by babl based on format description\n" -"\n"); - - fprintf (stderr, "*WARNING* missing babl fast path(s): \"%s\" to \"%s\"\n", - babl_get_name (source), - babl_get_name (destination)); - - } -} + if (path_error - 1.0 <= legal_error ) + /* check this before the more accurate measurement of error - + to bail earlier, this also leads to a stricter + discarding of bad fast paths */ +#endif + { + FishPathInstrumentation fpi; + memset (&fpi, 0, sizeof (fpi)); + fpi.source = (Babl*) babl_list_get_first (pc->current_path)->conversion.source; + fpi.destination = pc->to_format; -static Babl * -babl_fish_path2 (const Babl *source, - const Babl *destination, - double tolerance) -{ - Babl *babl = NULL; - const Babl *sRGB = babl_space ("sRGB"); - char name[BABL_MAX_NAME_LEN]; - int is_fast = 0; - static int debug_missing = -1; - if (debug_missing < 0) - { - const char *val = getenv ("BABL_DEBUG_MISSING"); - if (val && strcmp (val, "0")) - debug_missing = 1; - else - debug_missing = 0; - } + get_path_instrumentation (&fpi, pc->current_path, &path_cost, &ref_cost, &path_error); + if(debug_conversions && current_length == 1) + fprintf (stderr, "%s error:%f cost:%f \n", + babl_get_name (pc->current_path->items[0]), path_error, path_cost); - _babl_fish_create_name (name, source, destination, 1); - babl_mutex_lock (babl_format_mutex); - babl = babl_db_exist_by_name (babl_fish_db (), name); + if ((path_cost < ref_cost) && /* do not use paths that took longer to compute than reference */ + (path_cost < pc->fish_path->fish_path.cost) && // best thus far + (path_error <= legal_error ) // within tolerance + ) + { + /* We have found the best path so far, + * let's copy it into our new fish */ + pc->fish_path->fish_path.cost = path_cost; + pc->fish_path->fish.error = path_error; + babl_list_copy (pc->current_path, + pc->fish_path->fish_path.conversion_list); + } - if (tolerance <= 0.0) - { - is_fast = 0; - tolerance = _babl_legal_error (); - } + destroy_path_instrumentation (&fpi); + } + } else - is_fast = 1; - - if (!is_fast) - { - if (babl) { - /* There is an instance already registered by the required name, - * returning the preexistent one instead. + /* + * we have to search deeper... */ - babl_mutex_unlock (babl_format_mutex); - return babl; - } - } + BablList *list; + int i; - if ((source->format.space != sRGB) || - (destination->format.space != sRGB)) - { - static const Babl *run_once[512]={NULL}; - int i; - int done = 0; + list = current_format->format.from_list; + if (list) + { + /* Mark the current format in conversion path as visited */ + current_format->format.visited = 1; - for (i = 0; run_once[i]; i++) - { - if (run_once[i] == source->format.space) - done |= 1; - else if (run_once[i] == destination->format.space) - done |= 2; - } + /* Iterate through unvisited formats from the current format ...*/ + for (i = 0; i < babl_list_size (list); i++) + { + Babl *next_conversion = BABL (list->items[i]); + Babl *next_format = BABL (next_conversion->conversion.destination); + if (!next_format->format.visited && !bad_idea (current_format, pc->to_format, next_format)) + { + /* next_format is not in the current path, we can pay a visit */ + babl_list_insert_last (pc->current_path, next_conversion); + get_conversion_path (pc, next_format, current_length + 1, max_length, legal_error); + babl_list_remove_last (pc->current_path); + } + } - /* source space not in initialization array */ - if ((done & 1) == 0 && (source->format.space != sRGB)) - { - run_once[i++] = source->format.space; - babl_conversion_class_for_each (alias_conversion, (void*)source->format.space); + /* Remove the current format from current path */ + current_format->format.visited = 0; + } + } +} - _babl_space_add_universal_rgb (source->format.space); - } +char * +_babl_fish_create_name (char *buf, + const Babl *source, + const Babl *destination, + int is_reference) +{ + /* fish names are intentionally kept short */ + snprintf (buf, BABL_MAX_NAME_LEN, "%s %p %p %i", "", + source, destination, is_reference); + return buf; +} - /* destination space not in initialization array */ - if ((done & 2) == 0 && (destination->format.space != source->format.space) && (destination->format.space != sRGB)) - { - run_once[i++] = destination->format.space; - babl_conversion_class_for_each (alias_conversion, (void*)destination->format.space); +int +_babl_fish_path_destroy (void *data); - _babl_space_add_universal_rgb (destination->format.space); - } +int +_babl_fish_path_destroy (void *data) +{ + Babl *babl=data; + if (babl->fish_path.u8_lut) + free (babl->fish_path.u8_lut); + babl->fish_path.u8_lut = NULL; + if (babl->fish_path.conversion_list) + babl_free (babl->fish_path.conversion_list); + babl->fish_path.conversion_list = NULL; + return 0; +} - if (!done && 0) - { - babl_conversion_class_for_each (show_item, (void*)source->format.space); - } +static int +show_item (Babl *babl, + void *user_data) +{ + BablConversion *conv = (void *)babl; + + if (conv->destination->class_type == BABL_FORMAT) + { + fprintf (stderr, "%s : %.12f\n", babl_get_name (babl), babl_conversion_error(conv)); } - babl = babl_calloc (1, sizeof (BablFishPath) + - strlen (name) + 1); - babl_set_destructor (babl, _babl_fish_path_destroy); + return 0; +} - babl->class_type = BABL_FISH_PATH; - babl->instance.id = babl_fish_get_id (source, destination); - babl->instance.name = ((char *) babl) + sizeof (BablFishPath); - strcpy (babl->instance.name, name); - babl->fish.source = source; - babl->fish.destination = destination; - babl->fish.pixels = 0; - babl->fish.error = BABL_MAX_COST_VALUE; - babl->fish_path.cost = BABL_MAX_COST_VALUE; - babl->fish_path.conversion_list = babl_list_init_with_size (BABL_HARD_MAX_PATH_LENGTH); +static int +alias_conversion (Babl *babl, + void *user_data) +{ + const Babl *sRGB = babl_space ("sRGB"); + BablConversion *conv = (void *)babl; + BablSpace *space = user_data; + + if ((conv->source->class_type == BABL_FORMAT) && + (conv->destination->class_type == BABL_FORMAT) && + (!babl_format_is_palette (conv->source)) && + (!babl_format_is_palette (conv->destination))) + { + if ((conv->source->format.space == sRGB) && + (conv->destination->format.space == sRGB)) + { + switch (conv->instance.class_type) + { + case BABL_CONVERSION_LINEAR: + babl_conversion_new ( + babl_format_with_space ( + (void*)conv->source->instance.name, (void*)space), + babl_format_with_space ( + (void*)conv->destination->instance.name, (void*)space), + "linear", conv->function.linear, + "data", conv->data, + NULL); + break; + case BABL_CONVERSION_PLANAR: + babl_conversion_new ( + babl_format_with_space ( + (void*)conv->source->instance.name, (void*)space), + babl_format_with_space ( + (void*)conv->destination->instance.name, (void*)space), + "planar", conv->function.planar, + "data", conv->data, + NULL); + break; + case BABL_CONVERSION_PLANE: + babl_conversion_new ( + babl_format_with_space ( + (void*)conv->source->instance.name, (void*)space), + babl_format_with_space ( + (void*)conv->destination->instance.name, (void*)space), + "plane", conv->function.plane, + "data", conv->data, + NULL); + break; + default: + break; + } + } + } + else + if ((conv->source->class_type == BABL_MODEL) && + (conv->destination->class_type == BABL_MODEL)) + { + if ((conv->source->model.space == sRGB) && + (conv->destination->model.space == sRGB)) + { + switch (conv->instance.class_type) + { + case BABL_CONVERSION_LINEAR: + babl_conversion_new ( + babl_remodel_with_space ( + (void*)conv->source, (void*)space), + babl_remodel_with_space ( + (void*)conv->destination, (void*)space), + "linear", conv->function.linear, + "data", conv->data, + NULL); + break; + case BABL_CONVERSION_PLANAR: + babl_conversion_new ( + babl_remodel_with_space ( + (void*)conv->source, (void*)space), + babl_remodel_with_space ( + (void*)conv->destination, (void*)space), + "planar", conv->function.planar, + "data", conv->data, + NULL); + break; + case BABL_CONVERSION_PLANE: + babl_conversion_new ( + babl_remodel_with_space ( + (void*)conv->source, (void*)space), + babl_remodel_with_space ( + (void*)conv->destination, (void*)space), + "plane", conv->function.plane, + "data", conv->data, + NULL); + break; + default: + break; + } + } + } + else + if ((conv->source->class_type == BABL_TYPE) && + (conv->destination->class_type == BABL_TYPE)) + { + } + return 0; +} + +void +_babl_fish_prepare_bpp (Babl *babl) +{ + const Babl *babl_source = babl->fish.source; + const Babl *babl_dest = babl->fish.destination; + + switch (babl_source->instance.class_type) + { + case BABL_FORMAT: + babl->fish_path.source_bpp = babl_source->format.bytes_per_pixel; + break; + case BABL_TYPE: + babl->fish_path.source_bpp = babl_source->type.bits / 8; + break; + default: + babl_log ("=eeek{%i}\n", + babl_source->instance.class_type - BABL_MAGIC); + } + + switch (babl_dest->instance.class_type) + { + case BABL_FORMAT: + babl->fish_path.dest_bpp = babl_dest->format.bytes_per_pixel; + break; + case BABL_TYPE: + babl->fish_path.dest_bpp = babl_dest->type.bits / 8; + break; + default: + babl_log ("-eeek{%i}\n", babl_dest->instance.class_type - BABL_MAGIC); + } +} + +void +_babl_fish_missing_fast_path_warning (const Babl *source, + const Babl *destination) +{ +#ifndef BABL_UNSTABLE + if (debug_conversions) +#endif + { + static int warnings = 0; + + if (_babl_legal_error() <= 0.0000000001) + return; + + if (warnings++ == 0) + fprintf (stderr, +"Missing fast-path babl conversion detected, Implementing missing babl fast paths\n" +"accelerates GEGL, GIMP and other software using babl, warnings are printed on\n" +"first occurance of formats used where a conversion has to be synthesized\n" +"programmatically by babl based on format description\n" +"\n"); + + fprintf (stderr, "*WARNING* missing babl fast path(s): \"%s\" to \"%s\"\n", + babl_get_name (source), + babl_get_name (destination)); + + } +} + + +static Babl * +babl_fish_path2 (const Babl *source, + const Babl *destination, + double tolerance) +{ + Babl *babl = NULL; + const Babl *sRGB = babl_space ("sRGB"); + char name[BABL_MAX_NAME_LEN]; + int is_fast = 0; + static int debug_missing = -1; + if (debug_missing < 0) + { + const char *val = getenv ("BABL_DEBUG_MISSING"); + if (val && strcmp (val, "0")) + debug_missing = 1; + else + debug_missing = 0; + } + + _babl_fish_create_name (name, source, destination, 1); + babl_mutex_lock (babl_format_mutex); + babl = babl_db_exist_by_name (babl_fish_db (), name); + + if (tolerance <= 0.0) + { + is_fast = 0; + tolerance = _babl_legal_error (); + } + else + is_fast = 1; + + if (!is_fast) + { + if (babl) + { + /* There is an instance already registered by the required name, + * returning the preexistent one instead. + */ + babl_mutex_unlock (babl_format_mutex); + return babl; + } + } + + if ((source->format.space != sRGB) || + (destination->format.space != sRGB)) + { + static const Babl *run_once[512]={NULL}; + int i; + int done = 0; + + for (i = 0; run_once[i]; i++) + { + if (run_once[i] == source->format.space) + done |= 1; + else if (run_once[i] == destination->format.space) + done |= 2; + } + + /* source space not in initialization array */ + if ((done & 1) == 0 && (source->format.space != sRGB)) + { + run_once[i++] = source->format.space; + babl_conversion_class_for_each (alias_conversion, (void*)source->format.space); + + _babl_space_add_universal_rgb (source->format.space); + } + + /* destination space not in initialization array */ + if ((done & 2) == 0 && (destination->format.space != source->format.space) && (destination->format.space != sRGB)) + { + run_once[i++] = destination->format.space; + babl_conversion_class_for_each (alias_conversion, (void*)destination->format.space); + + _babl_space_add_universal_rgb (destination->format.space); + } + + if (!done && 0) + { + babl_conversion_class_for_each (show_item, (void*)source->format.space); + } + } + + babl = babl_calloc (1, sizeof (BablFishPath) + + strlen (name) + 1); + babl_set_destructor (babl, _babl_fish_path_destroy); + + babl->class_type = BABL_FISH_PATH; + babl->instance.id = babl_fish_get_id (source, destination); + babl->instance.name = ((char *) babl) + sizeof (BablFishPath); + strcpy (babl->instance.name, name); + babl->fish.source = source; + babl->fish.destination = destination; + babl->fish.pixels = 0; + babl->fish.error = BABL_MAX_COST_VALUE; + babl->fish_path.cost = BABL_MAX_COST_VALUE; + babl->fish_path.conversion_list = babl_list_init_with_size (BABL_HARD_MAX_PATH_LENGTH); @@ -656,14 +989,17 @@ babl_fish_path2 (const Babl *source, } _babl_fish_prepare_bpp (babl); + { + int source_bpp = babl->fish_path.source_bpp; + int dest_bpp = babl->fish_path.dest_bpp; if (//source->format.space != destination->format.space && ( - (babl->fish_path.source_bpp == 4 && babl->fish_path.dest_bpp == 16) - ||(babl->fish_path.source_bpp == 4 && babl->fish_path.dest_bpp == 4) - ||(babl->fish_path.source_bpp == 3 && babl->fish_path.dest_bpp == 4) - ||(babl->fish_path.source_bpp == 2 && babl->fish_path.dest_bpp == 4) - ||(babl->fish_path.source_bpp == 1 && babl->fish_path.dest_bpp == 4) - ||(babl->fish_path.source_bpp == 3 && babl->fish_path.dest_bpp == 3) + (source_bpp == 4 && dest_bpp == 16) + ||(source_bpp == 4 && dest_bpp == 4) + ||(source_bpp == 3 && dest_bpp == 4) + ||(source_bpp == 2 && dest_bpp == 4) + ||(source_bpp == 1 && dest_bpp == 4) + ||(source_bpp == 3 && dest_bpp == 3) ) ) { @@ -672,9 +1008,28 @@ babl_fish_path2 (const Babl *source, // exact data. Thus this is valid for instance for "YA half" if ((babl->conversion.source->format.type[0]->bits < 32 && - (babl->fish_path.source_bpp < 4 + (source_bpp < 4 || (source->format.model->flags & BABL_MODEL_FLAG_ASSOCIATED)==0))) - babl->fish_path.is_u8_color_conv = 1; + { + static int measured_timings = 0; + if (!measured_timings) measure_timings (); + measured_timings = 1; + fprintf (stderr, "%sLUT for %s to %s %i %f\n", + + (lut_timing_for (source_bpp, dest_bpp) * 12 < + babl->fish_path.cost)?"":"no ", + + babl_get_name (babl->conversion.source), + babl_get_name (babl->conversion.destination), + lut_timing_for (source_bpp, dest_bpp) * 12, + babl->fish_path.cost); + if (lut_timing_for (source_bpp, dest_bpp) * 12 < + babl->fish_path.cost) + { + babl->fish_path.is_u8_color_conv = 1; + } + } + } } _babl_fish_rig_dispatch (babl); @@ -721,283 +1076,6 @@ babl_fish_path (const Babl *source, return babl_fish_path2 (source, destination, 0.0); } -typedef struct GcContext { - long time; -} GcContext; - -static int gc_fishes (Babl *babl, void *userdata) -{ - GcContext *context = userdata; - if (babl->class_type == BABL_FISH_PATH) - { - if (babl->fish_path.u8_lut) - { - if (context->time - babl->fish_path.last_lut_use > - 1000 * 1000 * 60 * 5) - { - void *lut =babl->fish_path.u8_lut; - BABL(babl)->fish_path.u8_lut = NULL; - free (lut); -#if 0 - fprintf (stderr, "freeing LUT %s to %s unused for >5 minutes\n", - babl_get_name (babl->conversion.source), - babl_get_name (babl->conversion.destination)); -#endif - } - } - } - return 0; -} - -static void -babl_gc_fishes (void) -{ - GcContext context; - context.time = babl_ticks (); - babl_fish_class_for_each (gc_fishes, &context); - //malloc_trim (0); - // is responsibility of higher layers -} - -#define BABL_LIKELY(x) __builtin_expect(!!(x), 1) -#define BABL_UNLIKELY(x) __builtin_expect(!!(x), 0) - -static int babl_fish_lut_process_maybe (const Babl *babl, - const char *source, - const char *destination, - long n, - void *data) -{ - int source_bpp = babl->fish_path.source_bpp; - int dest_bpp = babl->fish_path.dest_bpp; - uint32_t *lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish.pixels += n; - - if (BABL_UNLIKELY(!lut && babl->fish.pixels >= 128 * 256)) - { -#if 0 - fprintf (stderr, "building LUT for %s to %s\n", - babl_get_name (babl->conversion.source), - babl_get_name (babl->conversion.destination)); -#endif - if (source_bpp ==4 && dest_bpp == 4) - { - lut = malloc (256 * 256 * 256 * 4); - for (int o = 0; o < 256 * 256 * 256; o++) - lut[o] = o; - process_conversion_path (babl->fish_path.conversion_list, - lut, 4, - lut, 4, - 256*256*256); - for (int o = 0; o < 256 * 256 * 256; o++) - lut[o] = lut[o] & 0x00ffffff; - } - else if (source_bpp == 4 && dest_bpp == 16) - { - uint32_t *temp_lut = malloc (256 * 256 * 256 * 4); - lut = malloc (256 * 256 * 256 * 16); - for (int o = 0; o < 256 * 256 * 256; o++) - temp_lut[o] = o; - process_conversion_path (babl->fish_path.conversion_list, - temp_lut, 4, - lut, 16, - 256*256*256); - free (temp_lut); - } - else if (source_bpp == 3 && dest_bpp == 3) - { - lut = malloc (256 * 256 * 256 * 4); - uint8_t *temp_lut = malloc (256 * 256 * 256 * 3); - uint8_t *temp_lut2 = malloc (256 * 256 * 256 * 3); - int o = 0; - for (int r = 0; r < 256; r++) - for (int g = 0; g < 256; g++) - for (int b = 0; b < 256; b++, o++) - { - temp_lut[o*3+0]=r; - temp_lut[o*3+1]=g; - temp_lut[o*3+2]=b; - } - process_conversion_path (babl->fish_path.conversion_list, - temp_lut, 3, - temp_lut2, 3, - 256*256*256); - babl_process (babl_fish (babl_format ("R'G'B' u8"), babl_format ("R'G'B'A u8")), - temp_lut2, lut, 256*256*256); - for (int o = 0; o < 256 * 256 * 256; o++) - lut[o] = lut[o] & 0x00ffffff; - free (temp_lut); - free (temp_lut2); - } - else if (source_bpp == 3 && dest_bpp == 4) - { - lut = malloc (256 * 256 * 256 * 4); - uint8_t *temp_lut = malloc (256 * 256 * 256 * 3); - int o = 0; - for (int r = 0; r < 256; r++) - for (int g = 0; g < 256; g++) - for (int b = 0; b < 256; b++, o++) - { - temp_lut[o*3+0]=r; - temp_lut[o*3+1]=g; - temp_lut[o*3+2]=b; - } - process_conversion_path (babl->fish_path.conversion_list, - temp_lut, 3, - lut, 4, - 256*256*256); - for (int o = 0; o < 256 * 256 * 256; o++) - lut[o] = lut[o] & 0x00ffffff; - free (temp_lut); - } - else if (source_bpp == 2 && dest_bpp == 4) - { - lut = malloc (256 * 256 * 4); - uint16_t *temp_lut = malloc (256 * 256 * 2); - for (int o = 0; o < 256*256; o++) - { - temp_lut[o]=o; - } - process_conversion_path (babl->fish_path.conversion_list, - temp_lut, 2, - lut, 4, - 256*256); - for (int o = 0; o < 256 * 256; o++) - lut[o] = lut[o] & 0x00ffffff; - free (temp_lut); - } - else if (source_bpp == 1 && dest_bpp == 4) - { - lut = malloc (256 * 4); - uint8_t *temp_lut = malloc (256); - for (int o = 0; o < 256; o++) - { - temp_lut[o]=o; - } - process_conversion_path (babl->fish_path.conversion_list, - temp_lut, 1, - lut, 4, - 256); - for (int o = 0; o < 256; o++) - lut[o] = lut[o] & 0x00ffffff; - free (temp_lut); - } - - if (babl->fish_path.u8_lut == NULL) - { - (BABL(babl)->fish_path.u8_lut) = lut; - // XXX need memory barrier? - if ((BABL(babl)->fish_path.u8_lut) != lut) - { - free (lut); - lut = babl->fish_path.u8_lut; - } - } - else - { - free (lut); - lut = babl->fish_path.u8_lut; - } - } - if (lut) - { - if (source_bpp == 4 && dest_bpp == 16) - { - uint32_t *src = (uint32_t*)source; - uint32_t *dst = (uint32_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - uint32_t col = *src++; - uint32_t lut_offset = col & 0xffffff; - float alpha = (col>>24)/255.0; - - *dst++ = lut[lut_offset*4+0]; - *dst++ = lut[lut_offset*4+1]; - *dst++ = lut[lut_offset*4+2]; - ((float*)(dst))[0] = alpha; - dst++; - } - return 1; - } - if (source_bpp == 4 && dest_bpp == 4) - { - uint32_t *src = (uint32_t*)source; - uint32_t *dst = (uint32_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - uint32_t col = *src++; - *dst = col & 0xff000000; - *dst |= lut[col & 0xffffff]; - dst++; - } - return 1; - } - if (source_bpp == 2 && dest_bpp == 4) - { - uint16_t *src = (uint16_t*)source; - uint32_t *dst = (uint32_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - *dst = lut[*src++]; - dst++; - } - return 1; - } - if (source_bpp == 1 && dest_bpp == 4) - { - uint8_t *src = (uint8_t*)source; - uint32_t *dst = (uint32_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - *dst = lut[*src++]; - dst++; - } - return 1; - } - else if (source_bpp == 3 && dest_bpp == 3) - { - uint8_t *src = (uint8_t*)source; - uint8_t *dst = (uint8_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - uint32_t col = src[0]*256*256+src[1]*256+src[2]; - uint32_t val = lut[col]; - dst[2]=(val >> 16) & 0xff; - dst[1]=(val >> 8) & 0xff; - dst[0]=val & 0xff; - dst+=3; - src+=3; - } - return 1; - } - else if (source_bpp == 3 && dest_bpp == 4) - { - uint8_t *src = (uint8_t*)source; - uint32_t *dst = (uint32_t*)destination; - lut = (uint32_t*)babl->fish_path.u8_lut; - BABL(babl)->fish_path.last_lut_use = babl_ticks (); - while (n--) - { - uint32_t col = src[0]*256*256+src[1]*256+src[2]; - *dst = lut[col]; - dst++; - src+=3; - } - return 1; - } - } - return 0; -} static void babl_fish_path_process (const Babl *babl, -- 2.30.2