/* babl - dynamically extendable universal pixel fish 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
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "babl-internal.h"
static Babl *
assert_conversion_find (const void *source,
const void *destination)
{
Babl *ret = babl_conversion_find (source, destination);
if (!ret)
babl_fatal ("failed finding conversion between %s and %s aborting",
babl_get_name (source), babl_get_name (destination));
return ret;
}
static int
create_name_internal (char *buf,
size_t maxlen,
const Babl *source,
const Babl *destination,
int is_reference)
{
return snprintf (buf, maxlen, "%s %p %p",
is_reference ? "ref "
: "",
source, destination);
}
#ifdef HAVE_TLS
static __thread char buf[1024];
static char *
create_name (const Babl *source,
const Babl *destination,
int is_reference)
{
int size = 0;
size = create_name_internal (buf, sizeof(buf), source, destination, is_reference);
if (size < 0)
return NULL;
return buf;
}
#else
static char *
create_name (const Babl *source,
const Babl *destination,
int is_reference)
{
int size = 0;
char *buf = NULL;
size = create_name_internal (buf, size, source, destination, is_reference);
if (size < 0)
return NULL;
size++; /* For '\0' */
buf = malloc (size);
if (buf == NULL)
return NULL;
size = create_name_internal (buf, size, source, destination, is_reference);
if (size < 0)
{
free (buf);
return NULL;
}
return buf;
}
#endif
Babl *
babl_fish_reference (const Babl *source,
const Babl *destination)
{
Babl *babl = NULL;
char *name = create_name (source, destination, 1);
babl_assert (name);
babl = babl_db_exist_by_name (babl_fish_db (), name);
if (babl)
{
/* There is an instance already registered by the required name,
* returning the preexistent one instead.
*/
#ifndef HAVE_TLS
free (name);
#endif
_babl_fish_rig_dispatch (babl);
return babl;
}
babl_assert (BABL_IS_BABL (source));
babl_assert (BABL_IS_BABL (destination));
babl_assert (source->class_type == BABL_FORMAT);
babl_assert (destination->class_type == BABL_FORMAT);
babl = babl_calloc (1, sizeof (BablFishReference) +
strlen (name) + 1);
babl->class_type = BABL_FISH_REFERENCE;
babl->instance.id = babl_fish_get_id (source, destination);
babl->instance.name = ((char *) babl) + sizeof (BablFishReference);
strcpy (babl->instance.name, name);
babl->fish.source = source;
babl->fish.destination = destination;
babl->fish.pixels = 0;
babl->fish.error = 0.0; /* assuming the provided reference conversions for types
and models are as exact as possible
*/
_babl_fish_rig_dispatch (babl);
/* Since there is not an already registered instance by the required
* name, inserting newly created class into database.
*/
babl_db_insert (babl_fish_db (), babl);
#ifndef HAVE_TLS
free (name);
#endif
return babl;
}
static void
convert_to_double (BablFormat *source_fmt,
const char *source_buf,
char *double_buf,
int n)
{
int i;
BablImage *src_img;
BablImage *dst_img;
src_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
dst_img->pitch[0] =
(dst_img->type[0]->bits / 8) * source_fmt->model->components;
dst_img->stride[0] = 0;
src_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
src_img->pitch[0] = source_fmt->bytes_per_pixel;
src_img->stride[0] = 0;
{
/* i is dest position */
for (i = 0; i < source_fmt->model->components; i++)
{
int j;
int found = 0;
dst_img->data[0] =
double_buf + (dst_img->type[0]->bits / 8) * i;
src_img->data[0] = (char *)source_buf;
/* j is source position */
for (j = 0; j < source_fmt->components; j++)
{
src_img->type[0] = source_fmt->type[j];
if (source_fmt->component[j] ==
source_fmt->model->component[i])
{
babl_conversion_process (assert_conversion_find (src_img->type[0], dst_img->type[0]),
(void*)src_img, (void*)dst_img, n);
found = 1;
break;
}
src_img->data[0] += src_img->type[0]->bits / 8;
}
if (!found)
{
char *dst_ptr = dst_img->data[0];
double value;
value = source_fmt->model->component[i]->instance.id == BABL_ALPHA ? 1.0 : 0.0;
for (j = 0; j < n; j++)
{
double *dst_component = (double *) dst_ptr;
*dst_component = value;
dst_ptr += dst_img->pitch[0];
}
}
}
}
babl_free (src_img);
babl_free (dst_img);
}
static void
convert_from_double (BablFormat *destination_fmt,
char *destination_double_buf,
char *destination_buf,
int n)
{
int i;
BablImage *src_img;
BablImage *dst_img;
src_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
src_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
src_img->pitch[0] = (src_img->type[0]->bits / 8) * destination_fmt->model->components;
src_img->stride[0] = 0;
dst_img->data[0] = destination_buf;
dst_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
dst_img->pitch[0] = destination_fmt->bytes_per_pixel;
dst_img->stride[0] = 0;
for (i = 0; i < destination_fmt->components; i++)
{
int j;
dst_img->type[0] = destination_fmt->type[i];
for (j = 0; j < destination_fmt->model->components; j++)
{
if (destination_fmt->component[i] ==
destination_fmt->model->component[j])
{
src_img->data[0] =
destination_double_buf + (src_img->type[0]->bits / 8) * j;
babl_conversion_process (assert_conversion_find (src_img->type[0],
dst_img->type[0]),
(void*)src_img, (void*)dst_img, n);
break;
}
}
dst_img->data[0] += dst_img->type[0]->bits / 8;
}
babl_free (src_img);
babl_free (dst_img);
}
static void
ncomponent_convert_to_double (BablFormat *source_fmt,
char *source_buf,
char *source_double_buf,
int n)
{
BablImage *src_img;
BablImage *dst_img;
src_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
dst_img->pitch[0] = (dst_img->type[0]->bits / 8);
dst_img->stride[0] = 0;
src_img->data[0] = source_buf;
src_img->type[0] = source_fmt->type[0];
src_img->pitch[0] = source_fmt->type[0]->bits / 8;
src_img->stride[0] = 0;
dst_img->data[0] = source_double_buf;
babl_conversion_process (
assert_conversion_find (src_img->type[0], dst_img->type[0]),
(void*)src_img, (void*)dst_img,
n * source_fmt->components);
babl_free (src_img);
babl_free (dst_img);
}
static void
ncomponent_convert_from_double (BablFormat *destination_fmt,
char *destination_double_buf,
char *destination_buf,
int n)
{
BablImage *src_img;
BablImage *dst_img;
src_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
dst_img = (BablImage *) babl_image_new (
babl_component_from_id (BABL_GRAY_LINEAR), NULL, 1, 0, NULL);
src_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
src_img->pitch[0] = (src_img->type[0]->bits / 8);
src_img->stride[0] = 0;
dst_img->data[0] = destination_buf;
dst_img->type[0] = (BablType *) babl_type_from_id (BABL_DOUBLE);
dst_img->pitch[0] = destination_fmt->type[0]->bits/8;
dst_img->stride[0] = 0;
dst_img->type[0] = destination_fmt->type[0];
src_img->data[0] = destination_double_buf;
babl_conversion_process (
assert_conversion_find (src_img->type[0], dst_img->type[0]),
(void*)src_img, (void*)dst_img,
n * destination_fmt->components);
dst_img->data[0] += dst_img->type[0]->bits / 8;
babl_free (src_img);
babl_free (dst_img);
}
static int
process_to_n_component (const Babl *babl,
const char *source,
char *destination,
long n)
{
void *double_buf;
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
int components = MAX(BABL (babl->fish.source)->format.model->components,
BABL (babl->fish.source)->format.components);
components = MAX(components, BABL (babl->fish.destination)->format.components);
components = MAX(components, BABL (babl->fish.destination)->model.components);
double_buf = babl_malloc (sizeof (double) * n * components);
memset (double_buf, 0,sizeof (double) * n * components);
{
ncomponent_convert_to_double (
(BablFormat *) BABL (babl->fish.source),
(char *) source,
double_buf,
n
);
ncomponent_convert_from_double (
(BablFormat *) BABL (babl->fish.destination),
double_buf,
(char *) destination,
n
);
}
babl_free (double_buf);
return 0;
}
static void
process_same_model (const Babl *babl,
const char *source,
char *destination,
long n)
{
void *double_buf;
#define MAX(a, b) ((a) > (b) ? (a) : (b))
double_buf = babl_malloc (sizeof (double) * n *
MAX (BABL (babl->fish.source)->format.model->components,
BABL (babl->fish.source)->format.components));
#undef MAX
if ((BABL (babl->fish.source)->format.components ==
BABL (babl->fish.destination)->format.components)
&& (BABL (babl->fish.source)->format.model->components !=
BABL (babl->fish.source)->format.components))
{
ncomponent_convert_to_double (
(BablFormat *) BABL (babl->fish.source),
(char *) source,
double_buf,
n
);
ncomponent_convert_from_double (
(BablFormat *) BABL (babl->fish.destination),
double_buf,
(char *) destination,
n
);
}
else
{
convert_to_double (
(BablFormat *) BABL (babl->fish.source),
(char *) source,
double_buf,
n
);
convert_from_double (
(BablFormat *) BABL (babl->fish.destination),
double_buf,
(char *) destination,
n
);
}
babl_free (double_buf);
}
void
babl_fish_reference_process (const Babl *babl,
const char *source,
char *destination,
long n,
void *data)
{
void *source_double_buf;
void *rgba_double_buf;
void *destination_double_buf;
Babl *source_image;
Babl *rgba_image;
Babl *destination_image;
if ((BABL (babl->fish.source)->format.model ==
BABL (babl->fish.destination)->format.model) &&
(BABL (babl->fish.source)->format.space ==
BABL (babl->fish.destination)->format.space)
)
{
process_same_model (babl, source, destination, n);
return;
}
if (babl_format_is_format_n (BABL (babl->fish.destination)))
{
process_to_n_component (babl, source, destination, n);
return;
}
source_double_buf = babl_malloc (sizeof (double) * n *
BABL (babl->fish.source)->format.model->components);
rgba_double_buf = babl_malloc (sizeof (double) * n * 4);
destination_double_buf = babl_malloc (sizeof (double) * n *
BABL (babl->fish.destination)->format.model->components);
source_image = babl_image_from_linear (
source_double_buf, BABL (BABL ((babl->fish.source))->format.model));
rgba_image = babl_image_from_linear (
rgba_double_buf, babl_remodel_with_space (babl_model_from_id (BABL_RGBA),
BABL (BABL ((babl->fish.source))->format.space)) );
destination_image = babl_image_from_linear (
destination_double_buf, BABL (BABL ((babl->fish.destination))->format.model));
convert_to_double (
(BablFormat *) BABL (babl->fish.source),
source,
source_double_buf,
n
);
{
Babl *conv =
assert_conversion_find (
BABL (babl->fish.source)->format.model,
babl_remodel_with_space (babl_model_from_id (BABL_RGBA),
BABL (BABL ((babl->fish.source))->format.space))
);
if (conv->class_type == BABL_CONVERSION_PLANAR)
{
babl_conversion_process (
conv,
(void*)source_image, (void*)rgba_image,
n);
}
else if (conv->class_type == BABL_CONVERSION_LINEAR)
{
babl_conversion_process (
conv,
source_double_buf, rgba_double_buf,
n);
}
else babl_fatal ("oops");
}
if (((babl->fish.source)->format.space !=
((babl->fish.destination)->format.space)))
{
double matrix[9];
double *rgba = rgba_double_buf;
babl_matrix_mul_matrix (
(babl->fish.destination)->format.space->space.XYZtoRGB,
(babl->fish.source)->format.space->space.RGBtoXYZ,
matrix);
babl_matrix_mul_vector_buf4 (matrix, rgba, rgba, n);
}
{
Babl *conv =
assert_conversion_find (
babl_remodel_with_space (babl_model_from_id (BABL_RGBA),
BABL (BABL ((babl->fish.destination))->format.space)),
BABL (babl->fish.destination)->format.model);
if (conv->class_type == BABL_CONVERSION_PLANAR)
{
babl_conversion_process (
conv,
(void*)rgba_image, (void*)destination_image,
n);
}
else if (conv->class_type == BABL_CONVERSION_LINEAR)
{
babl_conversion_process (
conv,
rgba_double_buf, destination_double_buf,
n);
}
else babl_fatal ("oops");
}
convert_from_double (
(BablFormat *) BABL (babl->fish.destination),
destination_double_buf,
destination,
n
);
babl_free (source_image);
babl_free (rgba_image);
babl_free (destination_image);
babl_free (destination_double_buf);
babl_free (rgba_double_buf);
babl_free (source_double_buf);
}