diff --git a/ChangeLog b/ChangeLog index 0a949df2..552bbdc8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,7 @@ - jpg, magick, png, tiff readers now use only 1 fd per input image - added vips_info_set(), vips_progress_set(), vips_profile_set() ... bindings can now support all the vips command-line options if they wish +- better conversion to greyscale, thanks bkw 26/3/15 started 7.42.4 - im_maxpos_avg() avoids NaN diff --git a/libvips/colour/LabQ2sRGB.c b/libvips/colour/LabQ2sRGB.c index 12d9b604..fd30656e 100644 --- a/libvips/colour/LabQ2sRGB.c +++ b/libvips/colour/LabQ2sRGB.c @@ -249,7 +249,7 @@ vips_col_XYZ2scRGB( float X, float Y, float Z, float *R, float *G, float *B ) return( 0 ); } -/* Turn scRGB into sRGB. Return or=1 for out of gamut - rgb will contain an +/* Turn scRGB into sRGB. Return og=1 for out of gamut - rgb will contain an * approximation of the right colour. * * Return -1 for NaN, Inf etc. @@ -258,11 +258,11 @@ static int vips_col_scRGB2sRGB( int range, int *lut, float R, float G, float B, int *r, int *g, int *b, - int *or_ret ) + int *og_ret ) { int maxval = range - 1; - int or; + int og; float Yf; int Yi; float v; @@ -283,16 +283,16 @@ vips_col_scRGB2sRGB( int range, int *lut, return( -1 ); } - /* Clip range, set the out-of-range flag. + /* Clip range, set the out-of-gamut flag. */ #define CLIP( L, V, H ) { \ if( (V) < (L) ) { \ (V) = (L); \ - or = 1; \ + og = 1; \ } \ if( (V) > (H) ) { \ (V) = (H); \ - or = 1; \ + og = 1; \ } \ } @@ -302,7 +302,7 @@ vips_col_scRGB2sRGB( int range, int *lut, * The +1 on the index is safe, see above. */ - or = 0; + og = 0; Yf = R * maxval; CLIP( 0, Yf, maxval ); @@ -322,31 +322,100 @@ vips_col_scRGB2sRGB( int range, int *lut, v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi); *b = VIPS_RINT( v ); - if( or_ret ) - *or_ret = or; + if( og_ret ) + *og_ret = og; return( 0 ); } int vips_col_scRGB2sRGB_8( float R, float G, float B, - int *r, int *g, int *b, - int *or ) + int *r, int *g, int *b, int *og ) { vips_col_make_tables_RGB_8(); - return( vips_col_scRGB2sRGB( 256, vips_Y2v_8, R, G, B, r, g, b, or ) ); + return( vips_col_scRGB2sRGB( 256, vips_Y2v_8, R, G, B, r, g, b, og ) ); } int vips_col_scRGB2sRGB_16( float R, float G, float B, - int *r, int *g, int *b, - int *or ) + int *r, int *g, int *b, int *og ) { vips_col_make_tables_RGB_16(); return( vips_col_scRGB2sRGB( 65536, vips_Y2v_16, - R, G, B, r, g, b, or ) ); + R, G, B, r, g, b, og ) ); +} + +/* Turn scRGB into BW. Return or=1 for out of gamut - g will contain an + * approximation of the right colour. + * + * Return -1 for NaN, Inf etc. + */ +static int +vips_col_scRGB2BW( int range, int *lut, float R, float G, float B, + int *g, int *og_ret ) +{ + int maxval = range - 1; + + float Y; + int og; + float Yf; + int Yi; + float v; + + /* RGB can be Nan, Inf etc. Throw those values out, they will break + * our clipping. + * + * Don't use isnormal(), it is false for 0.0 and for subnormal + * numbers. + */ + if( isnan( R ) || isinf( R ) || + isnan( G ) || isinf( G ) || + isnan( B ) || isinf( B ) ) { + *g = 0; + + return( -1 ); + } + + /* The usual ratio. We do this in linear space before we gamma. + */ + Y = 0.2 * R + 0.7 * G + 0.1 * B; + + /* Look up with a float index: interpolate between the nearest two + * points. + * + * The +1 on the index is safe, see above. + */ + + og = 0; + + Yf = Y * maxval; + CLIP( 0, Yf, maxval ); + Yi = (int) Yf; + v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi); + *g = VIPS_RINT( v ); + + if( og_ret ) + *og_ret = og; + + return( 0 ); +} + +int +vips_col_scRGB2BW_16( float R, float G, float B, int *g, int *og ) +{ + vips_col_make_tables_RGB_16(); + + return( vips_col_scRGB2BW( 65536, vips_Y2v_16, R, G, B, g, og ) ); +} + +int +vips_col_scRGB2BW_8( float R, float G, float B, int *g, int *og ) +{ + vips_col_make_tables_RGB_8(); + + return( vips_col_scRGB2BW( 256, vips_Y2v_8, R, G, B, g, og ) ); } /* Build Lab->disp dither tables. diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am index c47ccc90..37670acc 100644 --- a/libvips/colour/Makefile.am +++ b/libvips/colour/Makefile.am @@ -27,6 +27,7 @@ libcolour_la_SOURCES = \ LabQ2sRGB.c \ sRGB2scRGB.c \ scRGB2XYZ.c \ + scRGB2BW.c \ XYZ2scRGB.c \ scRGB2sRGB.c diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index 0e15e44d..014a2203 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -738,6 +738,7 @@ vips_colour_operation_init( void ) extern GType vips_XYZ2sRGB_get_type( void ); extern GType vips_sRGB2scRGB_get_type( void ); extern GType vips_scRGB2XYZ_get_type( void ); + extern GType vips_scRGB2BW_get_type( void ); extern GType vips_XYZ2scRGB_get_type( void ); extern GType vips_scRGB2sRGB_get_type( void ); #if defined(HAVE_LCMS) || defined(HAVE_LCMS2) @@ -769,6 +770,7 @@ vips_colour_operation_init( void ) vips_LabQ2sRGB_get_type(); vips_sRGB2scRGB_get_type(); vips_scRGB2XYZ_get_type(); + vips_scRGB2BW_get_type(); vips_XYZ2scRGB_get_type(); vips_scRGB2sRGB_get_type(); #if defined(HAVE_LCMS) || defined(HAVE_LCMS2) diff --git a/libvips/colour/colourspace.c b/libvips/colour/colourspace.c index 87390949..0bd36bb4 100644 --- a/libvips/colour/colourspace.c +++ b/libvips/colour/colourspace.c @@ -16,6 +16,9 @@ * - 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 */ /* @@ -73,6 +76,12 @@ vips_scRGB2RGB16( VipsImage *in, VipsImage **out, ... ) return( vips_scRGB2sRGB( in, out, "depth", 16, NULL ) ); } +static int +vips_scRGB2BW16( VipsImage *in, VipsImage **out, ... ) +{ + return( vips_scRGB2BW16( 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. */ @@ -140,25 +149,6 @@ vips_process_n( const char *domain, VipsImage *in, VipsImage **out, return( 0 ); } -static int -vips_sRGB2BW_op( VipsImage *in, VipsImage **out, ... ) -{ - if( vips_extract_band( in, out, 1, NULL ) ) - return( -1 ); - - return( 0 ); -} - -static int -vips_sRGB2BW( VipsImage *in, VipsImage **out, ... ) -{ - if( vips_process_n( "sRGB2BW", in, out, 3, vips_sRGB2BW_op ) ) - return( -1 ); - (*out)->Type = VIPS_INTERPRETATION_B_W; - - return( 0 ); -} - static int vips_BW2sRGB_op( VipsImage *in, VipsImage **out, ... ) { @@ -183,16 +173,6 @@ vips_BW2sRGB( VipsImage *in, VipsImage **out, ... ) return( 0 ); } -static int -vips_RGB162GREY16( VipsImage *in, VipsImage **out, ... ) -{ - if( vips_process_n( "RGB162GREY16", in, out, 3, vips_sRGB2BW_op ) ) - return( -1 ); - (*out)->Type = VIPS_INTERPRETATION_GREY16; - - return( 0 ); -} - static int vips_GREY162RGB16( VipsImage *in, VipsImage **out, ... ) { @@ -242,10 +222,9 @@ static VipsColourRoute vips_colour_routes[] = { { XYZ, LABS, { vips_XYZ2Lab, vips_Lab2LabS, NULL } }, { XYZ, scRGB, { vips_XYZ2scRGB, NULL } }, { XYZ, sRGB, { vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, - { XYZ, BW, { vips_XYZ2scRGB, vips_scRGB2sRGB, vips_sRGB2BW, NULL } }, + { XYZ, BW, { vips_XYZ2scRGB, vips_scRGB2BW, NULL } }, { XYZ, RGB16, { vips_XYZ2scRGB, vips_scRGB2RGB16, NULL } }, - { XYZ, GREY16, { vips_XYZ2scRGB, vips_scRGB2RGB16, - vips_RGB162GREY16, NULL } }, + { XYZ, GREY16, { vips_XYZ2scRGB, vips_scRGB2BW16, NULL } }, { XYZ, YXY, { vips_XYZ2Yxy, NULL } }, { LAB, XYZ, { vips_Lab2XYZ, NULL } }, @@ -255,12 +234,11 @@ static VipsColourRoute vips_colour_routes[] = { { LAB, LABS, { vips_Lab2LabS, NULL } }, { LAB, scRGB, { vips_Lab2XYZ, vips_XYZ2scRGB, NULL } }, { LAB, sRGB, { vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, - { LAB, BW, { vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, - vips_sRGB2BW, 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_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + vips_scRGB2BW16, NULL } }, { LAB, YXY, { vips_Lab2XYZ, vips_XYZ2Yxy, NULL } }, { LABQ, XYZ, { vips_LabQ2Lab, vips_Lab2XYZ, NULL } }, @@ -270,11 +248,12 @@ static VipsColourRoute vips_colour_routes[] = { { LABQ, LABS, { vips_LabQ2LabS, NULL } }, { LABQ, scRGB, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2scRGB } }, { LABQ, sRGB, { vips_LabQ2sRGB, NULL } }, - { LABQ, BW, { vips_LabQ2sRGB, vips_sRGB2BW, 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_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + vips_scRGB2BW16, NULL } }, { LABQ, YXY, { vips_LabQ2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } }, { LCH, XYZ, { vips_LCh2Lab, vips_Lab2XYZ, NULL } }, @@ -286,11 +265,11 @@ static VipsColourRoute vips_colour_routes[] = { { LCH, sRGB, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, { LCH, BW, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, - vips_scRGB2sRGB, vips_sRGB2BW, NULL } }, + vips_scRGB2BW, NULL } }, { LCH, RGB16, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2RGB16, NULL } }, { LCH, GREY16, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, - vips_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + vips_scRGB2BW16, NULL } }, { LCH, YXY, { vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } }, { CMC, XYZ, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ, NULL } }, @@ -303,11 +282,11 @@ static VipsColourRoute vips_colour_routes[] = { { CMC, sRGB, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, { CMC, BW, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ, - vips_XYZ2scRGB, vips_scRGB2sRGB, vips_sRGB2BW, NULL } }, + 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_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + vips_XYZ2scRGB, vips_scRGB2BW16, NULL } }, { CMC, YXY, { vips_CMC2LCh, vips_LCh2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } }, @@ -320,11 +299,11 @@ static VipsColourRoute vips_colour_routes[] = { { LABS, sRGB, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, { LABS, BW, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, - vips_scRGB2sRGB, vips_sRGB2BW, NULL } }, + vips_scRGB2BW, NULL } }, { LABS, RGB16, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, vips_scRGB2RGB16, NULL } }, { LABS, GREY16, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2scRGB, - vips_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + vips_scRGB2BW16, NULL } }, { LABS, YXY, { vips_LabS2Lab, vips_Lab2XYZ, vips_XYZ2Yxy, NULL } }, { scRGB, XYZ, { vips_scRGB2XYZ, NULL } }, @@ -334,10 +313,10 @@ static VipsColourRoute vips_colour_routes[] = { { scRGB, CMC, { vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } }, { scRGB, sRGB, { vips_scRGB2sRGB, NULL } }, - { scRGB, BW, { vips_scRGB2sRGB, vips_sRGB2BW, NULL } }, + { scRGB, BW, { vips_scRGB2BW, NULL } }, { scRGB, LABS, { vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, { scRGB, RGB16, { vips_scRGB2RGB16, NULL } }, - { scRGB, GREY16, { vips_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + { scRGB, GREY16, { vips_scRGB2BW16, NULL } }, { scRGB, YXY, { vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, { sRGB, XYZ, { vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } }, @@ -349,11 +328,11 @@ static VipsColourRoute vips_colour_routes[] = { { sRGB, CMC, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } }, { sRGB, scRGB, { vips_sRGB2scRGB, NULL } }, - { sRGB, BW, { vips_sRGB2BW, 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_sRGB2RGB16, vips_RGB162GREY16, NULL } }, + { sRGB, GREY16, { vips_sRGB2scRGB, vips_scRGB2BW16, NULL } }, { sRGB, YXY, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, { RGB16, XYZ, { vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } }, @@ -366,10 +345,10 @@ static VipsColourRoute vips_colour_routes[] = { vips_Lab2LCh, vips_LCh2CMC, NULL } }, { RGB16, scRGB, { vips_sRGB2scRGB, NULL } }, { RGB16, sRGB, { vips_RGB162sRGB, NULL } }, - { RGB16, BW, { vips_RGB162sRGB, vips_sRGB2BW, NULL } }, + { RGB16, BW, { vips_sRGB2scRGB, vips_scRGB2BW, NULL } }, { RGB16, LABS, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, - { RGB16, GREY16, { vips_RGB162GREY16, NULL } }, + { RGB16, GREY16, { vips_sRGB2scRGB, vips_scRGB2BW16, NULL } }, { RGB16, YXY, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, { GREY16, XYZ, { vips_GREY162RGB16, vips_sRGB2scRGB, @@ -384,8 +363,8 @@ static VipsColourRoute vips_colour_routes[] = { vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } }, { GREY16, scRGB, { vips_GREY162RGB16, vips_sRGB2scRGB, NULL } }, { GREY16, sRGB, { vips_GREY162RGB16, vips_RGB162sRGB, NULL } }, - { GREY16, BW, { vips_GREY162RGB16, vips_RGB162sRGB, - vips_sRGB2BW, 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 } }, @@ -406,8 +385,8 @@ static VipsColourRoute vips_colour_routes[] = { { 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_sRGB2RGB16, - vips_RGB162GREY16, NULL } }, + { BW, GREY16, { vips_BW2sRGB, vips_sRGB2scRGB, + vips_scRGB2BW16, NULL } }, { BW, YXY, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, @@ -420,12 +399,10 @@ static VipsColourRoute vips_colour_routes[] = { { YXY, LABS, { vips_Yxy2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, { YXY, scRGB, { vips_Yxy2XYZ, vips_XYZ2scRGB, NULL } }, { YXY, sRGB, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, NULL } }, - { YXY, BW, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2sRGB, - vips_sRGB2BW, 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_scRGB2RGB16, vips_RGB162GREY16, NULL } } + { YXY, GREY16, { vips_Yxy2XYZ, vips_XYZ2scRGB, vips_scRGB2BW16, NULL } } }; /* Is an image in a supported colourspace. diff --git a/libvips/colour/scRGB2BW.c b/libvips/colour/scRGB2BW.c new file mode 100644 index 00000000..fe530a86 --- /dev/null +++ b/libvips/colour/scRGB2BW.c @@ -0,0 +1,294 @@ +/* Turn scRGB into greyscale. + * + * 17/4/15 + * - from scRGB2BW.c + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "pcolour.h" + +/* We can't use VipsColourCode as our parent class. We want to handle + * alpha ourselves so we can get 16 -> 8 bit conversion right. + */ + +typedef struct _VipsscRGB2BW { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + int depth; +} VipsscRGB2BW; + +typedef VipsOperationClass VipsscRGB2BWClass; + +G_DEFINE_TYPE( VipsscRGB2BW, vips_scRGB2BW, VIPS_TYPE_OPERATION ); + +/* Process a buffer of data. + */ +static void +vips_scRGB2BW_line_8( VipsPel * restrict q, float * restrict p, + int extra_bands, int width ) +{ + int i, j; + + for( i = 0; i < width; i++ ) { + float R = p[0]; + float G = p[1]; + float B = p[2]; + + int g; + int or; + + vips_col_scRGB2BW_8( R, G, B, &g, &or ); + + p += 3; + + q[0] = g; + + q += 1; + + for( j = 0; j < extra_bands; j++ ) + q[j] = p[j]; + p += extra_bands; + q += extra_bands; + } +} + +static void +vips_scRGB2BW_line_16( unsigned short * restrict q, float * restrict p, + int extra_bands, int width ) +{ + int i, j; + + for( i = 0; i < width; i++ ) { + float R = p[0]; + float G = p[1]; + float B = p[2]; + + int g; + int or; + + vips_col_scRGB2BW_16( R, G, B, &g, &or ); + + p += 3; + + q[0] = g; + + q += 1; + + for( j = 0; j < extra_bands; j++ ) + q[j] = VIPS_CLIP( 0, p[j] * 256.0, USHRT_MAX ); + p += extra_bands; + q += extra_bands; + } +} + +static int +vips_scRGB2BW_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + VipsscRGB2BW *scRGB2BW = (VipsscRGB2BW *) b; + VipsRect *r = &or->valid; + VipsImage *in = ir->im; + + int y; + + if( vips_region_prepare( ir, r ) ) + return( -1 ); + + VIPS_GATE_START( "vips_scRGB2BW_gen: work" ); + + for( y = 0; y < r->height; y++ ) { + float *p = (float *) + VIPS_REGION_ADDR( ir, r->left, r->top + y ); + VipsPel *q = (VipsPel *) + VIPS_REGION_ADDR( or, r->left, r->top + y ); + + if( scRGB2BW->depth == 16 ) + vips_scRGB2BW_line_16( (unsigned short *) q, p, + in->Bands - 3, r->width ); + else + vips_scRGB2BW_line_8( q, p, + in->Bands - 3, r->width ); + } + + VIPS_GATE_STOP( "vips_scRGB2BW_gen: work" ); + + return( 0 ); +} + +static int +vips_scRGB2BW_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsscRGB2BW *scRGB2BW = (VipsscRGB2BW *) object; + + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsBandFormat format; + VipsInterpretation interpretation; + VipsImage *out; + + if( VIPS_OBJECT_CLASS( vips_scRGB2BW_parent_class )->build( object ) ) + return( -1 ); + + in = scRGB2BW->in; + if( vips_check_bands_atleast( class->nickname, in, 3 ) ) + return( -1 ); + + switch( scRGB2BW->depth ) { + case 16: + interpretation = VIPS_INTERPRETATION_GREY16; + format = VIPS_FORMAT_USHORT; + break; + + case 8: + interpretation = VIPS_INTERPRETATION_B_W; + format = VIPS_FORMAT_UCHAR; + break; + + default: + vips_error( class->nickname, + "%s", _( "depth must be 8 or 16" ) ); + return( -1 ); + } + + if( vips_cast_float( in, &t[0], NULL ) ) + return( -1 ); + in = t[0]; + + out = vips_image_new(); + if( vips_image_pipelinev( out, + VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) { + g_object_unref( out ); + return( -1 ); + } + out->Type = interpretation; + out->BandFmt = format; + out->Bands = in->Bands - 2; + + if( vips_image_generate( out, + vips_start_one, vips_scRGB2BW_gen, vips_stop_one, + in, scRGB2BW ) ) { + g_object_unref( out ); + return( -1 ); + } + + g_object_set( object, "out", out, NULL ); + + return( 0 ); +} + +static void +vips_scRGB2BW_class_init( VipsscRGB2BWClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "scRGB2BW"; + object_class->description = _( "convert scRGB to BW" ); + object_class->build = vips_scRGB2BW_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsscRGB2BW, in ) ); + + VIPS_ARG_IMAGE( class, "out", 100, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsscRGB2BW, out ) ); + + VIPS_ARG_INT( class, "depth", 130, + _( "Depth" ), + _( "Output device space depth in bits" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsscRGB2BW, depth ), + 8, 16, 8 ); + +} + +static void +vips_scRGB2BW_init( VipsscRGB2BW *scRGB2BW ) +{ + scRGB2BW->depth = 8; +} + +/** + * vips_scRGB2BW: + * @in: input image + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @depth: depth of output image in bits + * + * Convert an scRGB image to sRGB. Set @depth to 16 to get 16-bit output. + * + * If @depth is 16, any extra channels after RGB are + * multiplied by 256. + * + * See also: vips_LabS2LabQ(), vips_sRGB2scRGB(), vips_rad2float(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_scRGB2BW( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "scRGB2BW", ap, in, out ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h index 7b3f4e49..b06b5817 100644 --- a/libvips/include/vips/colour.h +++ b/libvips/include/vips/colour.h @@ -143,6 +143,8 @@ int vips_XYZ2scRGB( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_scRGB2sRGB( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_scRGB2BW( VipsImage *in, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_sRGB2scRGB( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_scRGB2XYZ( VipsImage *in, VipsImage **out, ... ) @@ -205,11 +207,11 @@ int vips_col_XYZ2scRGB( float X, float Y, float Z, float *R, float *G, float *B ); int vips_col_scRGB2sRGB_8( float R, float G, float B, - int *r, int *g, int *b, - int *or_ret ); + int *r, int *g, int *b, int *og ); int vips_col_scRGB2sRGB_16( float R, float G, float B, - int *r, int *g, int *b, - int *or_ret ); + int *r, int *g, int *b, int *og ); +int vips_col_scRGB2BW_16( float R, float G, float B, int *g, int *og ); +int vips_col_scRGB2BW_8( float R, float G, float B, int *g, int *og ); float vips_pythagoras( float L1, float a1, float b1, float L2, float a2, float b2 );