From 8f7c2c7110d1423f5d0e59fe7cda8c7c79bdc456 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 12 Feb 2015 17:50:38 +0000 Subject: [PATCH] sort out alpha going to and from 16-bit rewritten sRGB <-> scRGB so that 16-bit alpha is scaled to float 8 --- ChangeLog | 2 +- Makefile.am | 27 +----- TODO | 39 ++------- configure.ac | 15 ---- doc/libvips-docs.xml | 2 +- libvips/colour/colour.c | 1 - libvips/colour/colourspace.c | 19 ++--- libvips/colour/sRGB2scRGB.c | 160 ++++++++++++++++++++++++++--------- libvips/colour/scRGB2sRGB.c | 156 +++++++++++++++++++++++++--------- python/Vips.py | 20 +++++ test/test_colour.py | 20 ++++- 11 files changed, 290 insertions(+), 171 deletions(-) diff --git a/ChangeLog b/ChangeLog index 81b990c6..7cd106c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ - add fliphor(), flipver(), rot90(), rot180(), rot270() convenience methods to Python - add shift option to cast -- better alpha handling for 16 <-> 8 bit colour conversions +- sRGB2scRGB and scRGB2sRGB scale 16-bit alpha to and from 8-bit 6/2/15 started 7.42.3 - bump version for back-compat ABI change diff --git a/Makefile.am b/Makefile.am index fd3fea60..e7307cff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,15 +17,6 @@ P8_COMPILE_DIR = P8_DIST_DIR = python endif -# turn docs on or off -if ENABLE_DOCS -D_DIST_DIR = -D_BUILD_DIR = doc -else -D_DIST_DIR = doc -D_BUILD_DIR = -endif - SUBDIRS = \ libvips \ libvipsCC \ @@ -36,8 +27,7 @@ SUBDIRS = \ doc \ test \ $(P_COMPILE_DIR) \ - $(P8_COMPILE_DIR) \ - $(D_BUILD_DIR) + $(P8_COMPILE_DIR) EXTRA_DIST = \ m4 \ @@ -51,30 +41,17 @@ EXTRA_DIST = \ depcomp \ README.md \ $(P_DIST_DIR) \ - $(P8_DIST_DIR) \ - $(D_DIST_DIR) + $(P8_DIST_DIR) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = vips.pc vipsCC.pc vips-cpp.pc -if ENABLE_DOCS -install-exec-hook: - -rm -rf ${DESTDIR}$(datadir)/doc/vips - $(mkinstalldirs) ${DESTDIR}$(datadir)/doc/vips - -cp -r ${top_srcdir}/doc/html ${top_srcdir}/doc/pdf ${DESTDIR}$(datadir)/doc/vips -endif - dist-hook: # make sure we don't get any .svn dirs from EXTRA_DIST # also "fred" gets left around occasionally -find $(distdir) -name .svn -exec rm -rf {} \; -find $(distdir) -name fred -exec rm {} \; -uninstall-hook: -# make sure we have write permission for 'rm' - -chmod -R u+w ${DESTDIR}$(datadir)/doc/vips - -rm -rf ${DESTDIR}$(datadir)/doc/vips - ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc --enable-introspection diff --git a/TODO b/TODO index b5257557..dbf71eaa 100644 --- a/TODO +++ b/TODO @@ -1,40 +1,14 @@ -- at the moment we have a policy of just ignoring extra bands - - we are proposing to change this to modify band 2, 4 or 5, if present, when - going to and from RGB16 or GREY16 - - if we make this change, we must support it for all transforms of this type, - not just for a couple, or it'll be incredibly confusing - - vips_process_n() could use cast(shift = TRUE) - - vips_colour_build() could use cast shift too - - - - -- use vips_sRGB2RGB16() - - -- check vips_sRGB2scRGB() with a 16-bit source ... what does it do for alpha? - - alpha is untouched ... rgb->0-1, alpha stays at 0-65535 - - maybe should move everything to 0-1? otherwise when we do scRGB2sRGB we'll - (effectively) do alpha * 256 - - but what if we then do scRGB -> XYZ (for example), should alpha stay 0 - 1? +- tiff pyramid builder: + + - only write level 0 once ... append levels 1 and up afterwards + + - try to copy levels 1 and up without decompress / recompress -- we have - - im.flip("horizontal") - - maybe add fliphor() and flipver() as convenience functions, cf. .sin() etc. - why can't we do @@ -50,7 +24,8 @@ im = Vips.Image.jpegload(sys.argv[1], access = "sequential") - nope, fails too + nope, fails too ... mysterious! + diff --git a/configure.ac b/configure.ac index cc79065f..0b908129 100644 --- a/configure.ac +++ b/configure.ac @@ -243,20 +243,6 @@ else fi AC_DEFINE_UNQUOTED(VIPS_ICC_DIR,"$profile_dir",[default directory for ICC profiles]) -# option to stop docs installing ... eg. freebsd likes this -AC_ARG_ENABLE(docs, - AS_HELP_STRING([--enable-docs], [install docs (default: yes)])) - -if test x"$enable_docs" != x"no"; then - AM_CONDITIONAL(ENABLE_DOCS, true) - enable_docs=yes -fi - -if test x"$enable_docs" != x"yes"; then - AM_CONDITIONAL(ENABLE_DOCS, false) - enable_docs=no -fi - # we want largefile support, if possible AC_SYS_LARGEFILE @@ -851,7 +837,6 @@ open files in binary mode: $vips_binary_open enable debug: $enable_debug build deprecated components: $enable_deprecated build docs with gtkdoc: $enable_gtk_doc -install docs: $enable_docs gobject introspection: $found_introspection build vips7 Python binding: $with_python install vips8 Python overrides: $enable_pyvips8 diff --git a/doc/libvips-docs.xml b/doc/libvips-docs.xml index 5a5769a6..e09aa138 100644 --- a/doc/libvips-docs.xml +++ b/doc/libvips-docs.xml @@ -9,7 +9,7 @@ VIPS Reference Manual - For VIPS 7.42.3. + For VIPS 8.0.0. The latest version of this documentation can be found on the VIPS website. diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index 014906cf..0e15e44d 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -402,7 +402,6 @@ vips_colour_build( VipsObject *object ) */ if( vips_cast( extra_bands[i], &t1, out->BandFmt, - "shift", TRUE, NULL ) ) { g_object_unref( out ); return( -1 ); diff --git a/libvips/colour/colourspace.c b/libvips/colour/colourspace.c index 21a980c4..671f9d8d 100644 --- a/libvips/colour/colourspace.c +++ b/libvips/colour/colourspace.c @@ -74,7 +74,7 @@ vips_scRGB2RGB16( VipsImage *in, VipsImage **out, ... ) } /* Do these two with a simple cast ... since we're just cast shifting, we can - * short-circuit the extra band processing in vips_colour_build(). + * short-circuit the extra band processing. */ static int @@ -353,9 +353,8 @@ static VipsColourRoute vips_colour_routes[] = { { sRGB, BW, { vips_sRGB2BW, NULL } }, { sRGB, LABS, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, - { sRGB, RGB16, { vips_sRGB2scRGB, vips_scRGB2RGB16, NULL } }, - { sRGB, GREY16, { vips_sRGB2scRGB, vips_scRGB2RGB16, - vips_RGB162GREY16, NULL } }, + { sRGB, RGB16, { vips_sRGB2RGB16, NULL } }, + { sRGB, GREY16, { vips_sRGB2RGB16, vips_RGB162GREY16, NULL } }, { sRGB, YXY, { vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, { RGB16, XYZ, { vips_sRGB2scRGB, vips_scRGB2XYZ, NULL } }, @@ -385,9 +384,8 @@ static VipsColourRoute vips_colour_routes[] = { { GREY16, CMC, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LCh, vips_LCh2CMC, NULL } }, { GREY16, scRGB, { vips_GREY162RGB16, vips_sRGB2scRGB, NULL } }, - { GREY16, sRGB, { vips_GREY162RGB16, vips_sRGB2scRGB, - vips_scRGB2sRGB, NULL } }, - { GREY16, BW, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2sRGB, + { GREY16, sRGB, { vips_GREY162RGB16, vips_RGB162sRGB, NULL } }, + { GREY16, BW, { vips_GREY162RGB16, vips_RGB162sRGB, vips_sRGB2BW, NULL } }, { GREY16, LABS, { vips_GREY162RGB16, vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, @@ -408,10 +406,9 @@ static VipsColourRoute vips_colour_routes[] = { { BW, sRGB, { vips_BW2sRGB, NULL } }, { BW, LABS, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Lab, vips_Lab2LabS, NULL } }, - { BW, RGB16, { vips_BW2sRGB, vips_sRGB2scRGB, - vips_scRGB2RGB16, NULL } }, - { BW, GREY16, { vips_BW2sRGB, vips_sRGB2scRGB, - vips_scRGB2RGB16, vips_RGB162GREY16, NULL } }, + { BW, RGB16, { vips_BW2sRGB, vips_sRGB2RGB16, NULL } }, + { BW, GREY16, { vips_BW2sRGB, vips_sRGB2RGB16, + vips_RGB162GREY16, NULL } }, { BW, YXY, { vips_BW2sRGB, vips_sRGB2scRGB, vips_scRGB2XYZ, vips_XYZ2Yxy, NULL } }, diff --git a/libvips/colour/sRGB2scRGB.c b/libvips/colour/sRGB2scRGB.c index 6894ea5e..720b393a 100644 --- a/libvips/colour/sRGB2scRGB.c +++ b/libvips/colour/sRGB2scRGB.c @@ -13,6 +13,8 @@ * - add 16-bit sRGB import * 11/12/12 * - cut about to make sRGB2scRGB.c + * 12/2/15 + * - add 16-bit alpha handling */ /* @@ -54,17 +56,28 @@ #include "pcolour.h" -typedef VipsColourCode VipssRGB2scRGB; -typedef VipsColourCodeClass VipssRGB2scRGBClass; +/* We can't use VipsColourCode as our parent class. We want to handle + * alpha ourselves so we can get 16 -> 8 bit conversion right. + */ -G_DEFINE_TYPE( VipssRGB2scRGB, vips_sRGB2scRGB, VIPS_TYPE_COLOUR_CODE ); +typedef struct _VipssRGB2scRGB { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; +} VipssRGB2scRGB; + +typedef VipsOperationClass VipssRGB2scRGBClass; + +G_DEFINE_TYPE( VipssRGB2scRGB, vips_sRGB2scRGB, VIPS_TYPE_OPERATION ); /* Convert a buffer of 8-bit pixels. */ static void -vips_sRGB2scRGB_line_8( float * restrict q, VipsPel * restrict p, int width ) +vips_sRGB2scRGB_line_8( float * restrict q, VipsPel * restrict p, + int extra_bands, int width ) { - int i; + int i, j; for( i = 0; i < width; i++ ) { int r = p[0]; @@ -82,6 +95,11 @@ vips_sRGB2scRGB_line_8( float * restrict q, VipsPel * restrict p, int width ) q[2] = B; q += 3; + + for( j = 0; j < extra_bands; j++ ) + q[j] = p[j]; + p += extra_bands; + q += extra_bands; } } @@ -89,9 +107,9 @@ vips_sRGB2scRGB_line_8( float * restrict q, VipsPel * restrict p, int width ) */ static void vips_sRGB2scRGB_line_16( float * restrict q, unsigned short * restrict p, - int width ) + int extra_bands, int width ) { - int i; + int i, j; for( i = 0; i < width; i++ ) { int r = p[0]; @@ -109,69 +127,127 @@ vips_sRGB2scRGB_line_16( float * restrict q, unsigned short * restrict p, q[2] = B; q += 3; + + for( j = 0; j < extra_bands; j++ ) + q[j] = p[j] / 256.0; + p += extra_bands; + q += extra_bands; } } -static void -vips_sRGB2scRGB_line( VipsColour *colour, - VipsPel *out, VipsPel **in, int width ) +static int +vips_sRGB2scRGB_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) { - if( colour->in[0]->BandFmt == VIPS_FORMAT_UCHAR ) - vips_sRGB2scRGB_line_8( (float *) out, - (VipsPel *) in[0], width ); - else - vips_sRGB2scRGB_line_16( (float *) out, - (unsigned short *) in[0], width ); + VipsRegion *ir = (VipsRegion *) seq; + VipsRect *r = &or->valid; + VipsImage *in = ir->im; + + int y; + + if( vips_region_prepare( ir, r ) ) + return( -1 ); + + VIPS_GATE_START( "vips_sRGB2scRGB_gen: work" ); + + for( y = 0; y < r->height; y++ ) { + VipsPel *p = VIPS_REGION_ADDR( ir, r->left, r->top + y ); + float *q = (float *) + VIPS_REGION_ADDR( or, r->left, r->top + y ); + + if( in->BandFmt == VIPS_FORMAT_UCHAR ) + vips_sRGB2scRGB_line_8( q, p, + in->Bands - 3, r->width ); + else + vips_sRGB2scRGB_line_16( q, (unsigned short *) p, + in->Bands - 3, r->width ); + } + + VIPS_GATE_STOP( "vips_sRGB2scRGB_gen: work" ); + + return( 0 ); } static int vips_sRGB2scRGB_build( VipsObject *object ) { - VipsColourCode *code = (VipsColourCode *) object; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipssRGB2scRGB *sRGB2scRGB = (VipssRGB2scRGB *) object; - if( code->in ) - code->input_format = - code->in->BandFmt == VIPS_FORMAT_USHORT ? - VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsImage *out; + VipsBandFormat format; if( VIPS_OBJECT_CLASS( vips_sRGB2scRGB_parent_class )-> build( object ) ) return( -1 ); + in = sRGB2scRGB->in; + if( vips_check_bands_atleast( class->nickname, in, 3 ) ) + return( -1 ); + + format = in->BandFmt == VIPS_FORMAT_USHORT ? + VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; + if( vips_cast( in, &t[0], format, 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 = VIPS_INTERPRETATION_scRGB; + out->BandFmt = VIPS_FORMAT_FLOAT; + + if( vips_image_generate( out, + vips_start_one, vips_sRGB2scRGB_gen, vips_stop_one, + in, sRGB2scRGB ) ) { + g_object_unref( out ); + return( -1 ); + } + + g_object_set( object, "out", out, NULL ); + return( 0 ); } static void vips_sRGB2scRGB_class_init( VipssRGB2scRGBClass *class ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsColourClass *colour_class = VIPS_COLOUR_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; object_class->nickname = "sRGB2scRGB"; object_class->description = _( "convert an sRGB image to scRGB" ); object_class->build = vips_sRGB2scRGB_build; - colour_class->process_line = vips_sRGB2scRGB_line; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipssRGB2scRGB, in ) ); + + VIPS_ARG_IMAGE( class, "out", 100, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipssRGB2scRGB, out ) ); + } static void vips_sRGB2scRGB_init( VipssRGB2scRGB *sRGB2scRGB ) { - VipsColour *colour = VIPS_COLOUR( sRGB2scRGB ); - VipsColourCode *code = VIPS_COLOUR_CODE( sRGB2scRGB ); - - colour->coding = VIPS_CODING_NONE; - colour->interpretation = VIPS_INTERPRETATION_scRGB; - colour->format = VIPS_FORMAT_FLOAT; - colour->input_bands = 3; - colour->bands = 3; - - code->input_coding = VIPS_CODING_NONE; - - /* The default. This can get changed above ^^ if we see a - * 16-bit input. - */ - code->input_format = VIPS_FORMAT_UCHAR; } /** @@ -180,9 +256,13 @@ vips_sRGB2scRGB_init( VipssRGB2scRGB *sRGB2scRGB ) * @out: output image * @...: %NULL-terminated list of optional named arguments * - * Convert an sRGB image to scRGB. + * Convert an sRGB image to scRGB. The input image can be 8 or 16-bit + * unsigned int. * - * See also: vips_scRGB2XYZ(), vips_rad2float(). + * If the input image is unsigned 16-bit, any extra channels after RGB are + * divided by 256. Thus, scRGB alpha is always 0 - 255.99. + * + * See also: vips_scRGB2XYZ(), vips_scRGB2sRGB(), vips_rad2float(). * * Returns: 0 on success, -1 on error. */ diff --git a/libvips/colour/scRGB2sRGB.c b/libvips/colour/scRGB2sRGB.c index ef00c959..1102d117 100644 --- a/libvips/colour/scRGB2sRGB.c +++ b/libvips/colour/scRGB2sRGB.c @@ -25,6 +25,8 @@ * - added 16-bit option * 11/12/12 * - cut about to make scRGB2sRGB.c + * 12/2/15 + * - add 16-bit alpha handling */ /* @@ -66,22 +68,29 @@ #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 _VipsscRGB2sRGB { - VipsColourCode parent_instance; - + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; int depth; } VipsscRGB2sRGB; -typedef VipsColourCodeClass VipsscRGB2sRGBClass; +typedef VipsOperationClass VipsscRGB2sRGBClass; -G_DEFINE_TYPE( VipsscRGB2sRGB, vips_scRGB2sRGB, VIPS_TYPE_COLOUR_CODE ); +G_DEFINE_TYPE( VipsscRGB2sRGB, vips_scRGB2sRGB, VIPS_TYPE_OPERATION ); /* Process a buffer of data. */ static void -vips_scRGB2sRGB_line_8( VipsPel * restrict q, float * restrict p, int width ) +vips_scRGB2sRGB_line_8( VipsPel * restrict q, float * restrict p, + int extra_bands, int width ) { - int i; + int i, j; for( i = 0; i < width; i++ ) { float R = p[0]; @@ -100,14 +109,19 @@ vips_scRGB2sRGB_line_8( VipsPel * restrict q, float * restrict p, int width ) q[2] = b; q += 3; + + for( j = 0; j < extra_bands; j++ ) + q[j] = p[j]; + p += extra_bands; + q += extra_bands; } } static void vips_scRGB2sRGB_line_16( unsigned short * restrict q, float * restrict p, - int width ) + int extra_bands, int width ) { - int i; + int i, j; for( i = 0; i < width; i++ ) { float R = p[0]; @@ -126,21 +140,47 @@ vips_scRGB2sRGB_line_16( unsigned short * restrict q, float * restrict p, q[2] = b; q += 3; + + 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 void -vips_scRGB2sRGB_line( VipsColour *colour, - VipsPel *out, VipsPel **in, int width ) +static int +vips_scRGB2sRGB_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) { - VipsscRGB2sRGB *scRGB2sRGB = (VipsscRGB2sRGB *) colour; + VipsRegion *ir = (VipsRegion *) seq; + VipsscRGB2sRGB *scRGB2sRGB = (VipsscRGB2sRGB *) b; + VipsRect *r = &or->valid; + VipsImage *in = ir->im; - if( scRGB2sRGB->depth == 16 ) - vips_scRGB2sRGB_line_16( (unsigned short *) out, - (float *) in[0], width ); - else - vips_scRGB2sRGB_line_8( (VipsPel *) out, - (float *) in[0], width ); + int y; + + if( vips_region_prepare( ir, r ) ) + return( -1 ); + + VIPS_GATE_START( "vips_scRGB2sRGB_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( scRGB2sRGB->depth == 16 ) + vips_scRGB2sRGB_line_16( (unsigned short *) q, p, + in->Bands - 3, r->width ); + else + vips_scRGB2sRGB_line_8( q, p, + in->Bands - 3, r->width ); + } + + VIPS_GATE_STOP( "vips_scRGB2sRGB_gen: work" ); + + return( 0 ); } static int @@ -148,17 +188,30 @@ vips_scRGB2sRGB_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsscRGB2sRGB *scRGB2sRGB = (VipsscRGB2sRGB *) object; - VipsColour *colour = VIPS_COLOUR( scRGB2sRGB ); + + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsBandFormat format; + VipsInterpretation interpretation; + VipsImage *out; + + if( VIPS_OBJECT_CLASS( vips_scRGB2sRGB_parent_class )->build( object ) ) + return( -1 ); + + in = scRGB2sRGB->in; + if( vips_check_bands_atleast( class->nickname, in, 3 ) ) + return( -1 ); switch( scRGB2sRGB->depth ) { case 16: - colour->interpretation = VIPS_INTERPRETATION_RGB16; - colour->format = VIPS_FORMAT_USHORT; + interpretation = VIPS_INTERPRETATION_RGB16; + format = VIPS_FORMAT_USHORT; break; case 8: - colour->interpretation = VIPS_INTERPRETATION_sRGB; - colour->format = VIPS_FORMAT_UCHAR; + interpretation = VIPS_INTERPRETATION_sRGB; + format = VIPS_FORMAT_UCHAR; break; default: @@ -167,8 +220,27 @@ vips_scRGB2sRGB_build( VipsObject *object ) return( -1 ); } - if( VIPS_OBJECT_CLASS( vips_scRGB2sRGB_parent_class )->build( object ) ) + 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; + + if( vips_image_generate( out, + vips_start_one, vips_scRGB2sRGB_gen, vips_stop_one, + in, scRGB2sRGB ) ) { + g_object_unref( out ); + return( -1 ); + } + + g_object_set( object, "out", out, NULL ); return( 0 ); } @@ -178,7 +250,7 @@ vips_scRGB2sRGB_class_init( VipsscRGB2sRGBClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsColourClass *colour_class = VIPS_COLOUR_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; @@ -187,7 +259,19 @@ vips_scRGB2sRGB_class_init( VipsscRGB2sRGBClass *class ) object_class->description = _( "convert an scRGB image to sRGB" ); object_class->build = vips_scRGB2sRGB_build; - colour_class->process_line = vips_scRGB2sRGB_line; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsscRGB2sRGB, in ) ); + + VIPS_ARG_IMAGE( class, "out", 100, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsscRGB2sRGB, out ) ); VIPS_ARG_INT( class, "depth", 130, _( "Depth" ), @@ -195,25 +279,12 @@ vips_scRGB2sRGB_class_init( VipsscRGB2sRGBClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsscRGB2sRGB, depth ), 8, 16, 8 ); + } static void vips_scRGB2sRGB_init( VipsscRGB2sRGB *scRGB2sRGB ) { - VipsColour *colour = VIPS_COLOUR( scRGB2sRGB ); - VipsColourCode *code = VIPS_COLOUR_CODE( scRGB2sRGB ); - - /* Just the default, can be overridden, see above. - */ - colour->coding = VIPS_CODING_NONE; - colour->interpretation = VIPS_INTERPRETATION_sRGB; - colour->format = VIPS_FORMAT_UCHAR; - colour->input_bands = 3; - colour->bands = 3; - - code->input_coding = VIPS_CODING_NONE; - code->input_format = VIPS_FORMAT_FLOAT; - scRGB2sRGB->depth = 8; } @@ -229,7 +300,10 @@ vips_scRGB2sRGB_init( VipsscRGB2sRGB *scRGB2sRGB ) * * Convert an scRGB image to sRGB. Set @depth to 16 to get 16-bit output. * - * See also: vips_LabS2LabQ(), vips_scRGB2sRGB(), vips_rad2float(). + * 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. */ diff --git a/python/Vips.py b/python/Vips.py index bb906368..0ce06eca 100644 --- a/python/Vips.py +++ b/python/Vips.py @@ -908,6 +908,26 @@ class Image(Vips.Image): """size x size median filter.""" return self.rank(size, size, (size * size) / 2) + def fliphor(self): + """Flip horizontally.""" + return self.flip(Vips.Direction.HORIZONTAL) + + def flipver(self): + """Flip vertically.""" + return self.flip(Vips.Direction.VERTICAL) + + def rot90(self): + """Rotate 90 degrees clockwise.""" + return self.rot(Vips.Angle.D90) + + def rot180(self): + """Rotate 180 degrees.""" + return self.rot(Vips.Angle.D180) + + def rot270(self): + """Rotate 270 degrees clockwise.""" + return self.rot(Vips.Angle.D270) + # we need different imageize rules for this operator ... we need to # imageize th and el to match each other first @add_doc(generate_docstring("ifthenelse")) diff --git a/test/test_colour.py b/test/test_colour.py index 9ecf1887..ab2f8df1 100755 --- a/test/test_colour.py +++ b/test/test_colour.py @@ -31,12 +31,13 @@ colour_colourspaces = [Vips.Interpretation.XYZ, Vips.Interpretation.LABS, Vips.Interpretation.SCRGB, Vips.Interpretation.SRGB, - Vips.Interpretation.RGB16, Vips.Interpretation.YXY] coded_colourspaces = [Vips.Interpretation.LABQ] -mono_colourspaces = [Vips.Interpretation.GREY16, - Vips.Interpretation.B_W] -all_colourspaces = colour_colourspaces + mono_colourspaces + coded_colourspaces +mono_colourspaces = [Vips.Interpretation.B_W] +sixteenbit_colourspaces = [Vips.Interpretation.GREY16, + Vips.Interpretation.RGB16] +all_colourspaces = colour_colourspaces + mono_colourspaces + \ + coded_colourspaces + sixteenbit_colourspaces # an expanding zip ... if either of the args is not a list, duplicate it down # the other @@ -119,9 +120,20 @@ class TestColour(unittest.TestCase): for col in colour_colourspaces + [Vips.Interpretation.LAB]: im = im.colourspace(col) self.assertEqual(im.interpretation, col) + + for i in range(0, 4): + l = im.extract_band(i).min() + h = im.extract_band(i).max() + self.assertAlmostEqual(l, h) + pixel = im.getpoint(10, 10) self.assertAlmostEqual(pixel[3], 42, places = 2) + # alpha won't be equal for RGB16, but it should be preserved if we go + # there and back + im = im.colourspace(Vips.Interpretation.RGB16) + im = im.colourspace(Vips.Interpretation.LAB) + before = test.getpoint(10, 10) after = im.getpoint(10, 10) self.assertAlmostEqualObjects(before, after, places = 1)