libvips/libvips/colour/colourspace.c
John Cupitt e48f45187b make RGB and sRGB synonmous
We had a half-baked idea that RGB could mean generic RGB space and sRGB
would mean strict sRGB interpretation.

Unfortunately, this did not work well in practice. For example,
`icc_transform("srgb")` would tag the result as RGB rather than sRGB
(the converter didn't know it was writing sRGB pixels, it just saw
conversion to RGB with an ICC profile), and then later stages would do
unnecessary icc_imports, or worse, fail.

This patch makes RGB and sRGB strict synonyms. If you want to treat an
RGB image as something other than sRGB, you'll need to do it by hand
with the icc_ functions.

See

https://github.com/libvips/pyvips/issues/144

46212e92b1 (r34904985)

https://github.com/libvips/libvips/issues/1494
2019-12-22 11:40:09 +00:00

698 lines
23 KiB
C

/* convert between colourspaces
*
* 6/11/12
* - add RGB16 as a destination
* 12/1/14
* - add B_W as a source / dest
* - add GREY16 as a source / dest
* - add RGB16 as a source
* 19/1/14
* - auto-decode RAD images
* 3/2/14
* - add "source_space", overrides source space guess
* 8/5/14
* - oops, don't treat RGB16 as sRGB
* 9/9/14
* - mono <-> rgb converters were not handling extra bands, thanks James
* 4/2/15
* - much faster RGB16->sRGB path
* 17/4/15
* - better conversion to greyscale, see
* https://github.com/lovell/sharp/issues/193
* 27/12/18
* - add CMYK conversions
*/
/*
This file is part of VIPS.
VIPS 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 2 of the License, or
(at your option) any later version.
This program 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 program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include "pcolour.h"
static int
vips_scRGB2RGB16( VipsImage *in, VipsImage **out, ... )
{
return( vips_scRGB2sRGB( in, out, "depth", 16, NULL ) );
}
static int
vips_scRGB2BW16( VipsImage *in, VipsImage **out, ... )
{
return( vips_scRGB2BW( in, out, "depth", 16, NULL ) );
}
/* Do these two with a simple cast ... since we're just cast shifting, we can
* short-circuit the extra band processing.
*/
static int
vips_RGB162sRGB( VipsImage *in, VipsImage **out, ... )
{
if( vips_cast( in, out, VIPS_FORMAT_UCHAR,
"shift", TRUE,
NULL ) )
return( -1 );
(*out)->Type = VIPS_INTERPRETATION_sRGB;
return( 0 );
}
static int
vips_sRGB2RGB16( VipsImage *in, VipsImage **out, ... )
{
if( vips_cast( in, out, VIPS_FORMAT_USHORT,
"shift", TRUE,
NULL ) )
return( -1 );
(*out)->Type = VIPS_INTERPRETATION_RGB16;
return( 0 );
}
/* Process the first @n bands with @fn, detach and reattach remaining bands.
*
* Also used by CMYK2XYZ and XYZ2CMYK.
*/
int
vips__colourspace_process_n( const char *domain,
VipsImage *in, VipsImage **out, int n, VipsColourTransformFn fn )
{
if( in->Bands > n ) {
VipsImage *scope = vips_image_new();
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( scope ), 4 );
if( vips_extract_band( in, &t[0], 0,
"n", n,
NULL ) ||
vips_extract_band( in, &t[1], n,
"n", in->Bands - n,
NULL ) ||
fn( t[0], &t[2], NULL ) ||
vips_cast( t[1], &t[3], t[2]->BandFmt,
NULL ) ||
vips_bandjoin2( t[2], t[3], out, NULL ) ) {
g_object_unref( scope );
return( -1 );
}
g_object_unref( scope );
}
else if( in->Bands == n ) {
if( fn( in, out, NULL ) )
return( -1 );
}
else {
vips_error( domain, "%s", _( "too few bands for operation" ) );
return( -1 );
}
return( 0 );
}
static int
vips_BW2sRGB_op( VipsImage *in, VipsImage **out, ... )
{
VipsImage *t[3];
t[0] = in;
t[1] = in;
t[2] = in;
if( vips_bandjoin( t, out, 3, NULL ) )
return( -1 );
return( 0 );
}
static int
vips_BW2sRGB( VipsImage *in, VipsImage **out, ... )
{
if( vips__colourspace_process_n( "BW2sRGB",
in, out, 1, vips_BW2sRGB_op ) )
return( -1 );
(*out)->Type = VIPS_INTERPRETATION_sRGB;
return( 0 );
}
static int
vips_GREY162RGB16( VipsImage *in, VipsImage **out, ... )
{
if( vips__colourspace_process_n( "GREY162RGB16",
in, out, 1, vips_BW2sRGB_op ) )
return( -1 );
(*out)->Type = VIPS_INTERPRETATION_RGB16;
return( 0 );
}
/* Maximum number of steps we allow in a route. 10 steps should be enough
* for anyone.
*/
#define MAX_STEPS (10)
/* A route between two colour spaces.
*/
typedef struct _VipsColourRoute {
VipsInterpretation from;
VipsInterpretation to;
VipsColourTransformFn route[MAX_STEPS + 1];
} VipsColourRoute;
/* Some defines to save typing. These are the colour spaces we support
* conversions between.
*/
#define XYZ VIPS_INTERPRETATION_XYZ
#define LAB VIPS_INTERPRETATION_LAB
#define LABQ VIPS_INTERPRETATION_LABQ
#define LCH VIPS_INTERPRETATION_LCH
#define CMC VIPS_INTERPRETATION_CMC
#define LABS VIPS_INTERPRETATION_LABS
#define CMYK VIPS_INTERPRETATION_CMYK
#define scRGB VIPS_INTERPRETATION_scRGB
#define sRGB VIPS_INTERPRETATION_sRGB
#define HSV VIPS_INTERPRETATION_HSV
#define RGB16 VIPS_INTERPRETATION_RGB16
#define GREY16 VIPS_INTERPRETATION_GREY16
#define YXY VIPS_INTERPRETATION_YXY
#define BW VIPS_INTERPRETATION_B_W
/* All the routes we know about.
*/
static VipsColourRoute vips_colour_routes[] = {
{ XYZ, LAB, { vips_XYZ2Lab, NULL } },
{ XYZ, LABQ, { vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ XYZ, LCH, { vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ XYZ, CMC, { vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ XYZ, LABS, { vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ XYZ, CMYK, { vips_XYZ2CMYK, NULL } },
{ XYZ, scRGB, { vips_XYZ2scRGB, NULL } },
{ XYZ, sRGB, { vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } },
{ XYZ, HSV, { vips_XYZ2scRGB, vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ XYZ, BW, { vips_XYZ2scRGB, vips_scRGB2BW, NULL } },
{ XYZ, RGB16, { vips_XYZ2scRGB, vips_scRGB2RGB16, NULL } },
{ XYZ, GREY16, { vips_XYZ2scRGB, vips_scRGB2BW16, NULL } },
{ XYZ, YXY, { vips_XYZ2Yxy, NULL } },
{ LAB, XYZ, { vips_Lab2XYZ, NULL } },
{ LAB, LABQ, { vips_Lab2LabQ, NULL } },
{ LAB, LCH, { vips_Lab2LCh, NULL } },
{ LAB, CMC, { vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ LAB, LABS, { vips_Lab2LabS, NULL } },
{ LAB, CMYK, { vips_Lab2XYZ, vips_XYZ2CMYK, NULL } },
{ LAB, scRGB, { vips_Lab2XYZ, vips_XYZ2scRGB, NULL } },
{ LAB, sRGB, { vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } },
{ LAB, HSV, { vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB,
vips_sRGB2HSV, NULL } },
{ LAB, BW, { vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2BW, NULL } },
{ LAB, RGB16, { vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ LAB, GREY16, { vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW16, NULL } },
{ LAB, YXY, { vips_Lab2XYZ, vips_XYZ2Yxy, NULL } },
{ LABQ, XYZ, { vips_LabQ2Lab, vips_Lab2XYZ, NULL } },
{ LABQ, LAB, { vips_LabQ2Lab, NULL } },
{ LABQ, LCH, { vips_LabQ2Lab, vips_Lab2LCh, NULL } },
{ LABQ, CMC, { vips_LabQ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ LABQ, LABS, { vips_LabQ2LabS, NULL } },
{ LABQ, CMYK, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2CMYK, NULL } },
{ LABQ, scRGB, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, NULL } },
{ LABQ, sRGB, { vips_LabQ2sRGB, NULL } },
{ LABQ, HSV, { vips_LabQ2sRGB, vips_sRGB2HSV, NULL } },
{ LABQ, BW, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW, NULL } },
{ LABQ, RGB16, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ LABQ, GREY16, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW16, NULL } },
{ LABQ, YXY, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } },
{ LCH, XYZ, { vips_LCh2Lab, vips_Lab2XYZ, NULL } },
{ LCH, LAB, { vips_LCh2Lab, NULL } },
{ LCH, LABQ, { vips_LCh2Lab, vips_Lab2LabQ, NULL } },
{ LCH, CMC, { vips_LCh2CMC, NULL } },
{ LCH, LABS, { vips_LCh2Lab, vips_Lab2LabS, NULL } },
{ LCH, CMYK, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2CMYK, NULL } },
{ LCH, scRGB, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, NULL } },
{ LCH, sRGB, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, NULL } },
{ LCH, HSV, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ LCH, BW, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW, NULL } },
{ LCH, RGB16, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ LCH, GREY16, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW16, NULL } },
{ LCH, YXY, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } },
{ CMC, XYZ, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ, NULL } },
{ CMC, LAB, { vips_CMC2LCh, vips_LCh2Lab, NULL } },
{ CMC, LABQ, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2LabQ, NULL } },
{ CMC, LCH, { vips_CMC2LCh, NULL } },
{ CMC, LABS, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2LabS, NULL } },
{ CMC, CMYK, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2CMYK, NULL } },
{ CMC, scRGB, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, NULL } },
{ CMC, sRGB, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } },
{ CMC, HSV, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ CMC, BW, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, vips_scRGB2BW, NULL } },
{ CMC, RGB16, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, vips_scRGB2RGB16, NULL } },
{ CMC, GREY16, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2scRGB, vips_scRGB2BW16, NULL } },
{ CMC, YXY, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ,
vips_XYZ2Yxy, NULL } },
{ LABS, XYZ, { vips_LabS2Lab, vips_Lab2XYZ, NULL } },
{ LABS, LAB, { vips_LabS2Lab, NULL } },
{ LABS, LABQ, { vips_LabS2LabQ, NULL } },
{ LABS, LCH, { vips_LabS2Lab, vips_Lab2LCh, NULL } },
{ LABS, CMC, { vips_LabS2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ LABS, CMYK, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2CMYK, NULL } },
{ LABS, scRGB, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, NULL } },
{ LABS, sRGB, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, NULL } },
{ LABS, HSV, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ LABS, BW, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW, NULL } },
{ LABS, RGB16, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ LABS, GREY16, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW16, NULL } },
{ LABS, YXY, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } },
{ scRGB, XYZ, { vips_scRGB2XYZ, NULL } },
{ scRGB, LAB, { vips_scRGB2XYZ, vips_XYZ2Lab, NULL } },
{ scRGB, LABQ, { vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ scRGB, LCH, { vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ scRGB, CMC, { vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ scRGB, CMYK, { vips_scRGB2XYZ, vips_XYZ2CMYK, NULL } },
{ scRGB, sRGB, { vips_scRGB2sRGB, NULL } },
{ scRGB, HSV, { vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ scRGB, BW, { vips_scRGB2BW, NULL } },
{ scRGB, LABS, { vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ scRGB, RGB16, { vips_scRGB2RGB16, NULL } },
{ scRGB, GREY16, { vips_scRGB2BW16, NULL } },
{ scRGB, YXY, { vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } },
{ CMYK, XYZ, { vips_CMYK2XYZ, NULL } },
{ CMYK, LAB, { vips_CMYK2XYZ, vips_XYZ2Lab, NULL } },
{ CMYK, LABQ, { vips_CMYK2XYZ, vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ CMYK, LCH, { vips_CMYK2XYZ, vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ CMYK, CMC, { vips_CMYK2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ CMYK, scRGB, { vips_CMYK2XYZ, vips_XYZ2scRGB, NULL } },
{ CMYK, sRGB, { vips_CMYK2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, NULL } },
{ CMYK, HSV, { vips_CMYK2XYZ, vips_XYZ2scRGB,
vips_scRGB2sRGB, vips_sRGB2HSV, NULL } },
{ CMYK, BW, { vips_CMYK2XYZ, vips_XYZ2scRGB, vips_scRGB2BW, NULL } },
{ CMYK, LABS, { vips_CMYK2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ CMYK, RGB16, { vips_CMYK2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ CMYK, GREY16, { vips_CMYK2XYZ, vips_XYZ2scRGB,
vips_scRGB2BW16, NULL } },
{ CMYK, YXY, { vips_CMYK2XYZ, vips_XYZ2Yxy, NULL } },
{ sRGB, XYZ, { vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } },
{ sRGB, LAB, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, NULL } },
{ sRGB, LABQ, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LabQ, NULL } },
{ sRGB, LCH, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, NULL } },
{ sRGB, CMC, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ sRGB, CMYK, { vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2CMYK, NULL } },
{ sRGB, scRGB, { vips_sRGB2scRGB, NULL } },
{ sRGB, HSV, { vips_sRGB2HSV, NULL } },
{ sRGB, BW, { vips_sRGB2scRGB, vips_scRGB2BW, NULL } },
{ sRGB, LABS, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LabS, NULL } },
{ sRGB, RGB16, { vips_sRGB2RGB16, NULL } },
{ sRGB, GREY16, { vips_sRGB2scRGB, vips_scRGB2BW16, NULL } },
{ sRGB, YXY, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } },
{ HSV, XYZ, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } },
{ HSV, LAB, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, NULL } },
{ HSV, LABQ, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ HSV, LCH, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ HSV, CMC, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ HSV, CMYK, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2CMYK, NULL } },
{ HSV, scRGB, { vips_HSV2sRGB, vips_sRGB2scRGB, NULL } },
{ HSV, sRGB, { vips_HSV2sRGB, NULL } },
{ HSV, BW, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2BW, NULL } },
{ HSV, LABS, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ HSV, RGB16, { vips_HSV2sRGB, vips_sRGB2RGB16, NULL } },
{ HSV, GREY16, { vips_HSV2sRGB, vips_sRGB2scRGB,
vips_scRGB2BW16, NULL } },
{ HSV, YXY, { vips_HSV2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Yxy, NULL } },
{ RGB16, XYZ, { vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } },
{ RGB16, LAB, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, NULL } },
{ RGB16, LABQ, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LabQ, NULL } },
{ RGB16, LCH, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, NULL } },
{ RGB16, CMC, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ RGB16, CMYK, { vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2CMYK, NULL } },
{ RGB16, scRGB, { vips_sRGB2scRGB, NULL } },
{ RGB16, sRGB, { vips_RGB162sRGB, NULL } },
{ RGB16, HSV, { vips_RGB162sRGB, vips_sRGB2HSV, NULL } },
{ RGB16, BW, { vips_sRGB2scRGB, vips_scRGB2BW, NULL } },
{ RGB16, LABS, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab,
vips_Lab2LabS, NULL } },
{ RGB16, GREY16, { vips_sRGB2scRGB, vips_scRGB2BW16, NULL } },
{ RGB16, YXY, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } },
{ GREY16, XYZ, { vips_GREY162RGB16, vips_sRGB2scRGB,
vips_scRGB2XYZ, NULL } },
{ GREY16, LAB, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, NULL } },
{ GREY16, LABQ, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ GREY16, LCH, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ GREY16, CMC, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ GREY16, CMYK, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2CMYK, NULL } },
{ GREY16, scRGB, { vips_GREY162RGB16, vips_sRGB2scRGB, NULL } },
{ GREY16, sRGB, { vips_GREY162RGB16, vips_RGB162sRGB, NULL } },
{ GREY16, HSV, { vips_GREY162RGB16, vips_RGB162sRGB,
vips_sRGB2HSV, NULL } },
{ GREY16, BW, { vips_GREY162RGB16, vips_sRGB2scRGB,
vips_scRGB2BW, NULL } },
{ GREY16, LABS, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ GREY16, RGB16, { vips_GREY162RGB16, NULL } },
{ GREY16, YXY, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Yxy, NULL } },
{ BW, XYZ, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } },
{ BW, LAB, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, NULL } },
{ BW, LABQ, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ BW, LCH, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ BW, CMC, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } },
{ BW, CMYK, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2CMYK, NULL } },
{ BW, scRGB, { vips_BW2sRGB, vips_sRGB2scRGB, NULL } },
{ BW, sRGB, { vips_BW2sRGB, NULL } },
{ BW, HSV, { vips_BW2sRGB, vips_sRGB2HSV, NULL } },
{ BW, LABS, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ BW, RGB16, { vips_BW2sRGB, vips_sRGB2RGB16, NULL } },
{ BW, GREY16, { vips_BW2sRGB, vips_sRGB2scRGB,
vips_scRGB2BW16, NULL } },
{ BW, YXY, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ,
vips_XYZ2Yxy, NULL } },
{ YXY, XYZ, { vips_Yxy2XYZ, NULL } },
{ YXY, LAB, { vips_Yxy2XYZ, vips_XYZ2Lab, NULL } },
{ YXY, LABQ, { vips_Yxy2XYZ, vips_XYZ2Lab, vips_Lab2LabQ, NULL } },
{ YXY, LCH, { vips_Yxy2XYZ, vips_XYZ2Lab, vips_Lab2LCh, NULL } },
{ YXY, CMC, { vips_Yxy2XYZ, vips_XYZ2Lab, vips_Lab2LCh,
vips_LCh2CMC, NULL } },
{ YXY, LABS, { vips_Yxy2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } },
{ YXY, CMYK, { vips_Yxy2XYZ, vips_XYZ2CMYK, NULL } },
{ YXY, scRGB, { vips_Yxy2XYZ, vips_XYZ2scRGB, NULL } },
{ YXY, sRGB, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } },
{ YXY, HSV, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB,
vips_sRGB2HSV, NULL } },
{ YXY, BW, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2BW, NULL } },
{ YXY, RGB16, { vips_Yxy2XYZ, vips_XYZ2scRGB,
vips_scRGB2RGB16, NULL } },
{ YXY, GREY16, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2BW16, NULL } }
};
/* Is an image in a supported colourspace.
*/
/**
* vips_colourspace_issupported:
* @image: input image
*
* Test if @image is in a colourspace that vips_colourspace() can process.
*
* Returns: %TRUE if @image is in a supported colourspace.
*/
gboolean
vips_colourspace_issupported( const VipsImage *image )
{
VipsInterpretation interpretation;
int i;
/* Treat RGB as sRGB. If you want some other treatment,
* you'll need to use the icc funcs.
*/
interpretation = vips_image_guess_interpretation( image );
if( interpretation == VIPS_INTERPRETATION_RGB )
interpretation = VIPS_INTERPRETATION_sRGB;
for( i = 0; i < VIPS_NUMBER( vips_colour_routes ); i++ )
if( vips_colour_routes[i].from == interpretation )
return( TRUE );
return( FALSE );
}
typedef struct _VipsColourspace {
VipsOperation parent_instance;
VipsImage *in;
VipsImage *out;
VipsInterpretation space;
VipsInterpretation source_space;
} VipsColourspace;
typedef VipsOperationClass VipsColourspaceClass;
G_DEFINE_TYPE( VipsColourspace, vips_colourspace, VIPS_TYPE_OPERATION );
static int
vips_colourspace_build( VipsObject *object )
{
VipsColourspace *colourspace = (VipsColourspace *) object;
int i, j;
VipsImage *x;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
VipsImage **pipe = (VipsImage **)
vips_object_local_array( object, MAX_STEPS );
VipsInterpretation interpretation;
/* Verify that all input args have been set.
*/
if( VIPS_OBJECT_CLASS( vips_colourspace_parent_class )->
build( object ) )
return( -1 );
x = colourspace->in;
/* Unpack radiance-coded images. We can't use interpretation for this,
* since rad images can be scRGB or XYZ.
*/
if( x->Coding == VIPS_CODING_RAD ) {
if( vips_rad2float( x, &t[0], NULL ) )
return( -1 );
x = t[0];
}
if( vips_object_argument_isset( object, "source_space" ) )
interpretation = colourspace->source_space;
else
interpretation = vips_image_guess_interpretation( x );
/* Treat RGB as sRGB. If you want some other treatment,
* you'll need to use the icc funcs.
*/
if( interpretation == VIPS_INTERPRETATION_RGB )
interpretation = VIPS_INTERPRETATION_sRGB;
/* No conversion necessary.
*/
if( interpretation == colourspace->space ) {
g_object_set( colourspace, "out", vips_image_new(), NULL );
return( vips_image_write( colourspace->in, colourspace->out ) );
}
for( i = 0; i < VIPS_NUMBER( vips_colour_routes ); i++ )
if( vips_colour_routes[i].from == interpretation &&
vips_colour_routes[i].to == colourspace->space )
break;
if( i == VIPS_NUMBER( vips_colour_routes ) ) {
vips_error( "vips_colourspace",
_( "no known route from '%s' to '%s'" ),
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
interpretation ),
vips_enum_nick( VIPS_TYPE_INTERPRETATION,
colourspace->space ) );
return( -1 );
}
for( j = 0; vips_colour_routes[i].route[j]; j++ ) {
if( vips_colour_routes[i].route[j]( x, &pipe[j], NULL ) )
return( -1 );
x = pipe[j];
}
g_object_set( colourspace, "out", vips_image_new(), NULL );
if( vips_image_write( x, colourspace->out ) )
return( -1 );
return( 0 );
}
static void
vips_colourspace_class_init( VipsColourspaceClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "colourspace";
vobject_class->description = _( "convert to a new colorspace" );
vobject_class->build = vips_colourspace_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_IMAGE( class, "in", 1,
_( "Input" ),
_( "Input image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsColourspace, in ) );
VIPS_ARG_IMAGE( class, "out", 2,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsColourspace, out ) );
VIPS_ARG_ENUM( class, "space", 6,
_( "Space" ),
_( "Destination color space" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsColourspace, space ),
VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB );
VIPS_ARG_ENUM( class, "source_space", 6,
_( "Source space" ),
_( "Source color space" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsColourspace, source_space ),
VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB );
}
static void
vips_colourspace_init( VipsColourspace *colourspace )
{
colourspace->source_space = VIPS_INTERPRETATION_sRGB;
}
/**
* vips_colourspace: (method)
* @in: input image
* @out: (out): output image
* @space: convert to this colour space
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @source_space: input colour space
*
* This operation looks at the interpretation field of @in (or uses
* @source_space, if set) and runs
* a set of colourspace conversion functions to move it to @space.
*
* For example, given an image tagged as #VIPS_INTERPRETATION_YXY, running
* vips_colourspace() with @space set to #VIPS_INTERPRETATION_LAB will
* convert with vips_Yxy2XYZ() and vips_XYZ2Lab().
*
* See also: vips_colourspace_issupported(),
* vips_image_guess_interpretation().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_colourspace( VipsImage *in, VipsImage **out,
VipsInterpretation space, ... )
{
va_list ap;
int result;
va_start( ap, space );
result = vips_call_split( "colourspace", ap, in, out, space );
va_end( ap );
return( result );
}