/* 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 const Babl *construct_double_format (const Babl *model);
static int
babl_model_destroy (void *data)
{
Babl *babl = data;
if (babl->model.from_list)
babl_free (babl->model.from_list);
return 0;
}
static char *
babl_model_create_name (int components,
BablComponent **component)
{
char *p = NULL;
while (components--)
{
p = babl_strcat(p, (*component)->instance.name);
component++;
}
return p;
}
static Babl *
model_new (const char *name,
const Babl *space,
int id,
int components,
BablComponent **component,
BablModelFlag flags,
const char *doc)
{
Babl *babl;
babl = babl_malloc (sizeof (BablModel) +
sizeof (BablComponent *) * (components) +
strlen (name) + 1);
babl_set_destructor (babl, babl_model_destroy);
babl->model.component = (void *) (((char *) babl) + sizeof (BablModel));
babl->instance.name = (void *) (((char *) babl->model.component) + sizeof (BablComponent *) * (components));
babl->class_type = BABL_MODEL;
babl->instance.id = id;
babl->instance.doc = doc;
babl->model.components = components;
babl->model.space = space;
babl->model.data = NULL;
babl->model.model = NULL;
babl->model.flags = flags;
strcpy (babl->instance.name, name);
memcpy (babl->model.component, component, sizeof (BablComponent *) * components);
babl->model.from_list = NULL;
return babl;
}
static int
is_model_duplicate (Babl *babl,
const Babl *space,
int components,
BablComponent **component)
{
int i;
if (babl->model.space != space)
return 0;
if (babl->model.components != components)
return 0;
for (i = 0; i < components; i++)
{
if (babl->model.component[i] != component[i])
return 0;
}
return 1;
}
const Babl *
babl_model_new (void *first_argument,
...)
{
va_list varg;
Babl *babl = NULL;
int id = 0;
int components = 0;
const char *arg = first_argument;
const char *assigned_name = NULL;
char *name = NULL;
const char *doc = NULL;
const Babl *space = babl_space ("sRGB");
BablComponent *component [BABL_MAX_COMPONENTS];
BablModelFlag flags = 0;
va_start (varg, first_argument);
while (1)
{
/* first, we assume arguments to be strings */
if (!strcmp (arg, "id"))
{
id = va_arg (varg, int);
}
else if (!strcmp (arg, "doc"))
{
doc = va_arg (varg, const char *);
}
else if (!strcmp (arg, "name"))
{
assigned_name = va_arg (varg, char *);
}
else if (!strcmp (arg, "gray"))
{
flags |= BABL_MODEL_FLAG_GRAY;
}
else if (!strcmp (arg, "CIE"))
{
flags |= BABL_MODEL_FLAG_CIE;
}
else if (!strcmp (arg, "rgb"))
{
flags |= BABL_MODEL_FLAG_RGB;
}
else if (!strcmp (arg, "cmyk"))
{
flags |= BABL_MODEL_FLAG_CMYK;
}
else if (!strcmp (arg, "inverted"))
{
flags |= BABL_MODEL_FLAG_INVERTED;
}
else if (!strcmp (arg, "associated"))
{
flags |= BABL_MODEL_FLAG_ASSOCIATED;
}
else if (!strcmp (arg, "alpha"))
{
flags |= BABL_MODEL_FLAG_ALPHA;
}
else if (!strcmp (arg, "linear"))
{
flags |= BABL_MODEL_FLAG_LINEAR;
}
else if (!strcmp (arg, "nonlinear"))
{
flags |= BABL_MODEL_FLAG_NONLINEAR;
}
else if (!strcmp (arg, "perceptual"))
{
flags |= BABL_MODEL_FLAG_PERCEPTUAL;
}
/* if we didn't point to a known string, we assume argument to be babl */
else if (BABL_IS_BABL (arg))
{
Babl *bablc = (Babl *) arg;
switch (bablc->class_type)
{
case BABL_COMPONENT:
if (components >= BABL_MAX_COMPONENTS)
{
babl_log ("maximum number of components (%i) exceeded for %s",
BABL_MAX_COMPONENTS,
assigned_name ? assigned_name : "(unnamed)");
}
component [components++] = (BablComponent *) bablc;
break;
case BABL_MODEL:
babl_log ("submodels not handled yet");
break;
case BABL_SPACE:
space = bablc;
break;
case BABL_TYPE:
case BABL_TYPE_INTEGER:
case BABL_TYPE_FLOAT:
case BABL_SAMPLING:
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_SIMPLE:
case BABL_FISH_REFERENCE:
case BABL_FISH_PATH:
case BABL_IMAGE:
case BABL_EXTENSION:
babl_log ("%s unexpected", babl_class_name (bablc->class_type));
break;
case BABL_SKY: /* shut up compiler */
break;
}
}
else
{
babl_fatal ("unhandled argument '%s' for babl_model '%s'",
arg, assigned_name ? assigned_name : "(unnamed)");
}
arg = va_arg (varg, char *);
if (!arg)
break;
}
va_end (varg);
if (assigned_name)
name = babl_strdup(assigned_name);
else
name = babl_model_create_name (components, component);
if (!components)
{
babl_log("no components specified for model '%s'", name);
goto out;
}
babl = babl_db_exist (db, id, name);
if (id && !babl && babl_db_exist (db, 0, name))
babl_fatal ("Trying to reregister BablModel '%s' with different id!", name);
if (! babl)
{
babl = model_new (name, space, id, components, component, flags, doc);
babl_db_insert (db, babl);
construct_double_format (babl);
}
else
{
if (!is_model_duplicate (babl, space, components, component))
babl_fatal ("BablModel '%s' already registered "
"with different components!", name);
}
out:
babl_free (name);
return babl;
}
#define TOLERANCE 0.001
static const Babl *
reference_format (void)
{
static const Babl *self = NULL;
if (!self)
self = babl_format_new (
babl_model ("RGBA"),
babl_type ("double"),
babl_component ("R"),
babl_component ("G"),
babl_component ("B"),
babl_component ("A"),
NULL);
return self;
}
static const Babl *
construct_double_format (const Babl *model)
{
const void *argument[44 + 1];
int args = 0;
int i;
if (model == babl_model_from_id (BABL_RGBA))
{
argument[args++] = "id";
argument[args++] = (void*) BABL_RGBA_DOUBLE;
}
argument[args++] = model;
argument[args++] = babl_type_from_id (BABL_DOUBLE);
for (i = 0; i < model->model.components; i++)
{
argument[args++] = model->model.component[i];
}
argument[args++] = NULL;
#define o(argno) argument[argno],
return babl_format_new (o (0) o (1) o (2) o (3)
o (4) o (5) o (6) o (7)
o (8) o (9) o (10) o (11)
o (12) o (13) o (14) o (15)
o (16) o (17) o (18) o (19)
o (20) o (21) o (22) o (23)
o (24) o (25) o (26) o (27)
o (28) o (29) o (30) o (31)
o (32) o (33) o (34) o (35)
o (36) o (37) o (38) o (39)
o (40) o (41) o (42) NULL);
#undef o
}
double
babl_model_is_symmetric (const Babl *cbabl)
{
Babl *babl = (Babl*)cbabl;
void *original;
double *clipped;
void *destination;
double *transformed;
int symmetric = 1;
const Babl *ref_fmt;
const Babl *fmt;
Babl *fish_to;
Babl *fish_from;
const int test_pixels = babl_get_num_model_test_pixels ();
const double *test = babl_get_model_test_pixels ();
ref_fmt = reference_format ();
fmt = construct_double_format (babl);
fish_to = babl_fish_reference (ref_fmt, fmt);
fish_from = babl_fish_reference (fmt, ref_fmt);
original = babl_calloc (1, 64 / 8 * babl->model.components * test_pixels);
clipped = babl_calloc (1, 64 / 8 * 4 * test_pixels);
destination = babl_calloc (1, 64 / 8 * babl->model.components * test_pixels);
transformed = babl_calloc (1, 64 / 8 * 4 * test_pixels);
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);
fish_to->fish.pixels -= test_pixels * 2;
fish_from->fish.pixels -= test_pixels * 2;
{
int i;
int log = 0;
for (i = 0; i < test_pixels; i++)
{
int j;
for (j = 0; j < 4; j++)
{
float tolerance = TOLERANCE;
/* this to adapt to value ranges outside 0-1 */
if (fabs(clipped[i*4+j]) > 1.0)
tolerance = fabs(clipped[i*4+j]) * TOLERANCE;
if (fabs (clipped[i *4 + j] - transformed[i * 4 + j]) > tolerance)
{
if (!log)
log = 1;
symmetric = 0;
}
}
if (log && log < 5)
{
babl_log ("%s", babl->instance.name);
babl_log ("\ttest: %2.3f %2.3f %2.3f %2.3f", test [i *4 + 0],
test [i * 4 + 1],
test [i * 4 + 2],
test [i * 4 + 3]);
babl_log ("\tclipped: %2.3f %2.3f %2.3f %2.3f", clipped [i *4 + 0],
clipped [i * 4 + 1],
clipped [i * 4 + 2],
clipped [i * 4 + 3]);
babl_log ("\ttrnsfrmd: %2.3f %2.3f %2.3f %2.3f", transformed [i *4 + 0],
transformed [i * 4 + 1],
transformed [i * 4 + 2],
transformed [i * 4 + 3]);
log++;
}
}
}
babl_free (original);
babl_free (clipped);
babl_free (destination);
babl_free (transformed);
return symmetric;
}
BABL_CLASS_IMPLEMENT (model)
/* XXX: probably better to do like with babl_format, add a -suffix and
* insert in normal database than to have this static cache list
*/
static const Babl *babl_remodels[512]={NULL,};
int babl_n_remodels = 0;
const Babl *
babl_remodel_with_space (const Babl *model,
const Babl *space)
{
Babl *ret;
int i;
assert (BABL_IS_BABL (model));
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;
}
if (model->model.space == space)
return (void*)model;
assert (BABL_IS_BABL (model));
/* get back to the sRGB model if we are in a COW clone of it */
if (model->model.model)
model = (void*)model->model.model;
assert (BABL_IS_BABL (model));
for (i = 0; i < babl_n_remodels; i++)
{
if (babl_remodels[i]->model.model == model &&
babl_remodels[i]->model.space == space)
return babl_remodels[i];
}
ret = babl_calloc (sizeof (BablModel), 1);
memcpy (ret, model, sizeof (BablModel));
ret->model.space = space;
ret->model.model = (void*)model; /* use the data as a backpointer to original model */
return babl_remodels[babl_n_remodels++] = ret;
return (Babl*)ret;
}
const Babl *
babl_model_with_space (const char *name,
const Babl *space)
{
return babl_remodel_with_space (babl_model (name), space);
}
BablModelFlag
babl_get_model_flags (const Babl *babl)
{
if (!babl) return 0;
switch (babl->class_type)
{
case BABL_MODEL:
return babl->model.flags;
case BABL_FORMAT:
return babl->format.model->flags;
}
return 0;
}