/* babl - dynamically extendable universal pixel conversion library. * Copyright (C) 2005, Øyvind Kolås. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see * . */ #include "config.h" #include #include #include #define NEEDS_BABL_DB #include "babl-internal.h" #include "babl-db.h" #include "babl-ref-pixels.h" static int babl_format_destruct (void *babl) { BablFormat *format = babl; if (format->image_template != NULL) { babl_set_destructor (format->image_template, NULL); /* with no destructor set, the circular reference is no problem */ babl_free (format->image_template); format->image_template = NULL; } if (format->from_list) babl_free (format->from_list); return 0; } static Babl * format_new (const char *name, int id, int planar, int components, BablModel *model, const Babl *space, BablComponent **component, BablSampling **sampling, const BablType **type, const char *doc) { Babl *babl; /* i is destination position */ int i, j, component_found = 0; for (i = 0; i < model->components; i++) { for (j = 0; j < components; j++) { if (component[j] == model->component[i]) { component_found = 1; break; } } if (!component_found) { component_found = 0; #if 0 /* since babl does the right thing, let it happen */ babl_fatal("matching source component for %s in model %s not found", model->component[i]->instance.name, model->instance.name); #endif } } /* allocate all memory in one chunk */ babl = babl_malloc (sizeof (BablFormat) + strlen (name) + 1 + sizeof (BablComponent *) * (components) + sizeof (BablSampling *) * (components) + sizeof (BablType *) * (components) + sizeof (int) * (components) + sizeof (int) * (components)); babl_set_destructor (babl, babl_format_destruct); babl->format.from_list = NULL; babl->format.component = (void *) (((char *) babl) + sizeof (BablFormat)); babl->format.type = (void *) (((char *) babl->format.component) + sizeof (BablComponent *) * (components)); babl->format.sampling = (void *) (((char *) babl->format.type) + sizeof (BablType *) * (components)); babl->instance.name = ((char *) babl->format.sampling) + sizeof (BablSampling *) * (components); babl->class_type = BABL_FORMAT; babl->instance.id = id; strcpy (babl->instance.name, name); babl->format.components = components; if (space == babl_space ("sRGB")) babl->format.model = model; else babl->format.model = (void*)babl_remodel_with_space ((void*)model, space); memcpy (babl->format.component, component, sizeof (BablComponent *) * components); memcpy (babl->format.type, type, sizeof (BablType *) * components); memcpy (babl->format.sampling, sampling, sizeof (BablSampling *) * components); babl->format.planar = planar; babl->format.bytes_per_pixel = 0; { int i; for (i = 0; i < components; i++) babl->format.bytes_per_pixel += type[i]->bits / 8; } babl->format.loss = -1.0; babl->format.visited = 0; babl->format.image_template = NULL; babl->format.format_n = 0; babl->format.palette = 0; babl->format.space = (void*)space; babl->format.encoding = NULL; babl->instance.doc = doc; return babl; } Babl * format_new_from_format_with_space (const Babl *format, const Babl *space) { Babl *ret; char new_name[256]; snprintf (new_name, sizeof (new_name), "%s-%s", babl_get_name ((void*)format), babl_get_name ((void*)space)); ret = babl_db_find (babl_format_db(), new_name); if (ret) return ret; ret = format_new (new_name, 0, format->format.planar, format->format.components, (void*)babl_remodel_with_space (BABL(format->format.model), space), space, format->format.component, format->format.sampling, (void*)format->format.type, NULL); ret->format.encoding = babl_get_name(format); babl_db_insert (db, (void*)ret); return ret; } static char * create_name (const BablModel *model, int components, BablComponent **component, const BablType **type) { char buf[512] = ""; char *p = &buf[0]; ssize_t left; int i; int same_types = 1; const BablType**t = type; const BablType *first_type = *type; BablComponent **c1 = component; BablComponent **c2 = model->component; left = 512; snprintf (p, left, "%s ", model->instance.name); p += strlen (model->instance.name) + 1; left -= strlen (model->instance.name) + 1; babl_assert (left >= 0); i = components; while (i--) { if (first_type != *t) { same_types = 0; break; } t++; } if (same_types && components != model->components) same_types = 0; i = components; while (same_types && i--) { if (*c1 != *c2) { same_types = 0; break; } c1++; c2++; } if (same_types) { snprintf (p, left, "%s", first_type->instance.name); return babl_strdup (buf); } i = components; while (i--) { snprintf (p, left, "(%s as %s) ", (*component)->instance.name, (*type)->instance.name); p += strlen ((*component)->instance.name) + strlen ((*type)->instance.name) + strlen ("( as ) "); left -= strlen ((*component)->instance.name) + strlen ((*type)->instance.name) + strlen ("( as ) "); babl_assert (left >= 0); component++; type++; } return babl_strdup (buf); } static char * ncomponents_create_name (const Babl *type, int components) { char buf[512]; snprintf (buf, sizeof (buf), "%s[%i] ", type->instance.name, components); return babl_strdup (buf); } static void babl_format_set_is_format_n (Babl *format) { if (format->class_type == BABL_FORMAT) { format->format.format_n = 1; } } const Babl * babl_format_n (const Babl *btype, int components) { int i; Babl *babl; int id = 0; int planar = 0; BablModel *model = (BablModel *)babl_model ("Y"); BablComponent *component [components]; BablSampling *sampling [components]; const BablType *type [components]; char *name = NULL; for (i = 0; icomponent[0]; type[i] = &btype->type; sampling[i] = (BablSampling *) babl_sampling (1, 1); } name = ncomponents_create_name (btype, components); babl = babl_db_exist (db, id, name); if (babl) { /* There is an instance already registered by the required id/name, * returning the preexistent one instead. */ babl_free (name); return babl; } babl = format_new (name, id, planar, components, model, babl_space("sRGB"), component, sampling, type, NULL); babl_format_set_is_format_n (babl); babl_db_insert (db, babl); babl_free (name); return babl; } int babl_format_is_format_n (const Babl *format) { if (format->class_type == BABL_FORMAT) { return format->format.format_n; } return 0; } static int is_format_duplicate (Babl *babl, int planar, int components, BablModel *model, const Babl *space, BablComponent **component, BablSampling **sampling, const BablType **type) { int i; if (babl->format.planar != planar || babl->format.components != components || babl->format.model != model || babl->format.space != (void*)space) return 0; for (i = 0; i < components; i++) { if (babl->format.component[i] != component[i] || babl->format.sampling[i] != sampling[i] || babl->format.type[i] != type[i]) return 0; } return 1; } const Babl * babl_format_new (const void *first_arg, ...) { va_list varg; Babl *babl; int id = 0; int planar = 0; int components = 0; BablModel *model = NULL; const Babl *space = babl_space ("sRGB"); const char *doc = NULL; BablComponent *component [BABL_MAX_COMPONENTS]; BablSampling *sampling [BABL_MAX_COMPONENTS]; const BablType*type [BABL_MAX_COMPONENTS]; BablSampling *current_sampling = (BablSampling *) babl_sampling (1, 1); BablType *current_type = (BablType *) babl_type_from_id (BABL_DOUBLE); char *name = NULL; const void *arg = first_arg; va_start (varg, first_arg); while (1) { /* first, we assume arguments to be strings */ if (!strcmp (arg, "id")) { id = va_arg (varg, int); } else if (!strcmp (arg, "name")) { name = babl_strdup (va_arg (varg, char *)); } else if (!strcmp (arg, "doc")) { doc = babl_strdup (va_arg (varg, const char *)); } else if (!strcmp (arg, "packed")) { planar = 0; } else if (!strcmp (arg, "planar")) { planar = 1; } /* if we didn't point to a known string, we assume argument to be babl */ else if (BABL_IS_BABL (arg)) { Babl *babl = (Babl *) arg; switch (babl->class_type) { case BABL_TYPE: case BABL_TYPE_FLOAT: case BABL_TYPE_INTEGER: current_type = (BablType *) babl; break; case BABL_COMPONENT: if (!model) { babl_fatal ("no model specified before component %s", babl->instance.name); } component [components] = (BablComponent *) babl; type [components] = current_type; sampling [components] = current_sampling; components++; if (components >= BABL_MAX_COMPONENTS) { babl_fatal ("maximum number of components (%i) exceeded for %s", BABL_MAX_COMPONENTS, name); } break; case BABL_SAMPLING: current_sampling = (BablSampling *) arg; break; case BABL_SPACE: space = (Babl*) arg; break; case BABL_MODEL: if (model) { babl_log ("args=(%s): model %s already requested", babl->instance.name, model->instance.name); } model = (BablModel *) arg; break; case BABL_INSTANCE: case BABL_FORMAT: case BABL_CONVERSION: case BABL_CONVERSION_LINEAR: case BABL_CONVERSION_PLANE: case BABL_CONVERSION_PLANAR: case BABL_FISH: case BABL_FISH_REFERENCE: case BABL_FISH_SIMPLE: case BABL_FISH_PATH: case BABL_IMAGE: case BABL_EXTENSION: babl_log ("%s unexpected", babl_class_name (babl->class_type)); break; case BABL_SKY: /* shut up compiler */ break; } } else { babl_fatal ("unhandled argument '%s' for format '%s'", arg, name); } arg = va_arg (varg, char *); if (!arg) break; } va_end (varg); if (!name) name = create_name (model, components, component, type); if (space != babl_space ("sRGB")) { char *new_name = babl_malloc (strlen (name) + strlen (babl_get_name ((Babl*)space)) + 1); sprintf (new_name, "%s-%s", name, babl_get_name ((Babl*)space)); babl_free (name); name = new_name; } if (!model) { babl_log ("no model specified for format '%s'", name); babl_free(name); return NULL; } if (!components) { babl_log ("no components specified for format '%s'", name); babl_free(name); return NULL; } babl = babl_db_exist (db, id, name); if (id && !babl && babl_db_exist (db, 0, name)) babl_fatal ("Trying to reregister BablFormat '%s' with different id!", name); if (babl) { /* There is an instance already registered by the required id/name, * returning the preexistent one instead if it doesn't differ. */ if(0)if (!is_format_duplicate (babl, planar, components, model, space, component, sampling, type)) babl_fatal ("BablFormat '%s' already registered " "with different content!", name); babl_free (name); return babl; } babl = format_new ((void*)name, id, planar, components, model, space, component, sampling, type, doc); babl_db_insert (db, babl); babl_free (name); return babl; } int babl_formats_count (void) { return babl_db_count (db); } int babl_format_has_alpha (const Babl *format) { int n = babl_format_get_n_components (format); int i; for (i = 0; i < n; i++) { if (format->format.component[i]->alpha) { return 1; } } return 0; } int babl_format_get_bytes_per_pixel (const Babl *format) { if (format->class_type == BABL_FORMAT) { return format->format.bytes_per_pixel; } return 0; } int babl_format_get_n_components (const Babl *format) { if (format->class_type == BABL_FORMAT) { return format->format.components; } return 0; } const Babl * babl_format_get_type (const Babl *format, int component_index) { if (format->class_type == BABL_FORMAT && component_index >= 0 && component_index < format->format.components) { return (Babl *)format->format.type[component_index]; } return NULL; } const Babl * babl_format_with_model_as_type (const Babl *model, const Babl *type) { BablComponent *component[10]; int i; for (i = 0; i < model->model.components; i++) { component[i] = model->model.component[i]; } component[i] = NULL; return babl_format_new ( model, type, component[0], component[1], component[2], component[3], component[4], component[5], component[6], component[7], component[8], component[9], NULL ); } double babl_format_loss (const Babl *babl) { double loss = 0.0; void *original; double *clipped; void *destination; double *transformed; const Babl *ref_fmt; const Babl *fmt; Babl *fish_to; Babl *fish_from; const double *test = babl_get_format_test_pixels (); const int test_pixels = babl_get_num_format_test_pixels (); ref_fmt = babl_format_new ( babl_model ("RGBA"), babl_type ("double"), babl_component ("R"), babl_component ("G"), babl_component ("B"), babl_component ("A"), NULL); if (babl->format.loss != -1.0) return babl->format.loss; fmt = babl; fish_to = babl_fish_reference (ref_fmt, fmt); fish_from = babl_fish_reference (fmt, ref_fmt); original = babl_calloc (test_pixels, fmt->format.bytes_per_pixel); clipped = babl_calloc (test_pixels, ref_fmt->format.bytes_per_pixel); destination = babl_calloc (test_pixels, fmt->format.bytes_per_pixel); transformed = babl_calloc (test_pixels, ref_fmt->format.bytes_per_pixel); babl_process (fish_to, test, original, test_pixels); babl_process (fish_from, original, clipped, test_pixels); babl_process (fish_to, clipped, destination, test_pixels); babl_process (fish_from, destination, transformed, test_pixels); loss = babl_rel_avg_error (clipped, test, test_pixels * 4); fish_to->fish.pixels -= test_pixels * 2; fish_from->fish.pixels -= test_pixels * 2; babl_free (original); babl_free (clipped); babl_free (destination); babl_free (transformed); ((Babl*)babl)->format.loss = loss; return loss; } void * babl_get_user_data (const Babl *babl) { switch (babl->instance.class_type) { case BABL_MODEL: return babl->model.data; case BABL_FORMAT: return babl->format.model->data; default: babl_fatal ("babl_get_user_data called on non-model/format"); } babl_fatal ("eeeek"); return NULL; } void babl_set_user_data (const Babl *cbabl, void *data) { Babl *babl = (Babl*) cbabl; switch (cbabl->instance.class_type) { case BABL_MODEL: babl->model.data = data; break; case BABL_FORMAT: babl->format.model->data = data; break; default: babl_fatal ("babl_set_user_data called on non-model/format"); } } const Babl * babl_format_get_model (const Babl *format) { if (format->class_type == BABL_FORMAT) { return (Babl*)format->format.model; } return NULL; } const Babl * babl_format_get_space (const Babl *format) { if (format->class_type == BABL_FORMAT) { return (Babl*)format->format.space; } return NULL; } BABL_CLASS_IMPLEMENT (format) const char * babl_format_get_encoding (const Babl *babl) { if (!babl) return NULL; if (babl->format.encoding) return babl->format.encoding; return babl_get_name (babl); } const Babl * babl_format_with_space (const char *encoding, const Babl *space) { const Babl *example_format = (void*) encoding; if (!encoding) return NULL; if (BABL_IS_BABL (example_format)) { encoding = babl_get_name (example_format); if (babl_format_get_space (example_format) != babl_space ("sRGB")) { encoding = babl_format_get_encoding (example_format); } } if (!space) space = babl_space ("sRGB"); if (space->class_type == BABL_FORMAT) { space = space->format.space; } else if (space->class_type == BABL_MODEL) { space = space->model.space; } else if (space->class_type != BABL_SPACE) { return NULL; } example_format = babl_format (encoding); if (space == babl_space("sRGB")) return example_format; if (babl_format_is_palette (example_format)) { /* XXX we should allocate a new palette name, and duplicate the path data, converted for new space */ return example_format; } return format_new_from_format_with_space (example_format, space); } int babl_format_exists (const char *name) { if (babl_db_exist_by_name (db, name)) return 1; return 0; }