e48f45187b
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
698 lines
23 KiB
C
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 );
|
|
}
|