From 3c60e9de6017ffc4ecdd1cd29da2cfe2bfa8b757 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 24 Feb 2021 14:48:50 +0000 Subject: [PATCH 01/11] start tinkering --- libvips/conversion/flatten.c | 2 +- libvips/resample/thumbnail.c | 158 ++++++++++++++++++++--------------- 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/libvips/conversion/flatten.c b/libvips/conversion/flatten.c index 912644c9..7c9f3527 100644 --- a/libvips/conversion/flatten.c +++ b/libvips/conversion/flatten.c @@ -67,7 +67,7 @@ typedef struct _VipsFlatten { */ VipsArrayDouble *background; - /* The [double] converted to the input image format. + /* The [double] background converted to the input image format. */ VipsPel *ink; diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 88b18d8a..6db6950f 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -626,13 +626,17 @@ vips_thumbnail_build( VipsObject *object ) int preshrunk_page_height; double hshrink; double vshrink; - VipsInterpretation interpretation; /* TRUE if we've done the import of an ICC transform and still need to * export. */ gboolean have_imported; + /* If we shrink in linear space, we need to return to the input + * colourspace after the shrink. + */ + VipsInterpretation input_interpretation; + /* TRUE if we've premultiplied and need to unpremultiply. */ gboolean have_premultiplied; @@ -669,7 +673,7 @@ vips_thumbnail_build( VipsObject *object ) */ preshrunk_page_height = vips_image_get_page_height( in ); - /* RAD needs special unpacking. + /* Coded forms need special unpacking. */ if( in->Coding == VIPS_CODING_RAD ) { g_info( "unpacking Rad to float" ); @@ -680,61 +684,68 @@ vips_thumbnail_build( VipsObject *object ) return( -1 ); in = t[12]; } + else if( in->Coding == VIPS_CODING_LABQ ) { + g_info( "unpacking LABQ to float" ); - /* In linear mode, we import right at the start. - * - * We also have to import the whole image if it's CMYK, since - * vips_colourspace() (see below) doesn't let you specify the fallback - * profile. - * - * This is only going to work for images in device space. If you have - * an image in PCS which also has an attached profile, strange things - * will happen. - */ - have_imported = FALSE; - if( thumbnail->linear && - in->Coding == VIPS_CODING_NONE && - (in->BandFmt == VIPS_FORMAT_UCHAR || - in->BandFmt == VIPS_FORMAT_USHORT) && - (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || - thumbnail->import_profile) ) { - g_info( "importing to XYZ PCS" ); - if( thumbnail->import_profile ) - g_info( "fallback input profile %s", - thumbnail->import_profile ); - - if( vips_icc_import( in, &t[1], - "input_profile", thumbnail->import_profile, - "embedded", TRUE, - "intent", thumbnail->intent, - "pcs", VIPS_PCS_XYZ, - NULL ) ) + if( vips_LabQ2Lab( in, &t[12], NULL ) ) return( -1 ); - - in = t[1]; - - have_imported = TRUE; + in = t[12]; } - /* To the processing colourspace. This will unpack LABQ, import CMYK, - * etc. + /* In linear mode, we need to transform to a linear space before + * vips_resize(). * - * If this is a CMYK image, we need to set have_imported since we only - * want to export at the end. + * If we are doing colour management (there's an import profile), + * then we use XYZ PCS as the resize space. */ - if( in->Type == VIPS_INTERPRETATION_CMYK ) - have_imported = TRUE; - if( thumbnail->linear ) - interpretation = VIPS_INTERPRETATION_scRGB; - else if( in->Bands < 3 ) - interpretation = VIPS_INTERPRETATION_B_W; - else - interpretation = VIPS_INTERPRETATION_sRGB; - g_info( "converting to processing space %s", - vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) ); - if( vips_colourspace( in, &t[2], interpretation, NULL ) ) - return( -1 ); - in = t[2]; + have_imported = FALSE; + if( thumbnail->linear ) { + if( in->Coding == VIPS_CODING_NONE && + (in->BandFmt == VIPS_FORMAT_UCHAR || + in->BandFmt == VIPS_FORMAT_USHORT) && + (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || + thumbnail->import_profile) ) { + g_info( "importing to XYZ PCS" ); + if( thumbnail->import_profile ) + g_info( "fallback input profile %s", + thumbnail->import_profile ); + + if( vips_icc_import( in, &t[1], + "input_profile", thumbnail->import_profile, + "embedded", TRUE, + "intent", thumbnail->intent, + "pcs", VIPS_PCS_XYZ, + NULL ) ) + return( -1 ); + + in = t[1]; + + have_imported = TRUE; + } + else { + /* Otherwise, use scRGB or GREY16 for linear shrink. + */ + VipsInterpretation interpretation; + + /* Note the interpretation we will revert to after + * linear. + */ + input_interpretation = in->Type; + + if( in->Bands < 3 ) + interpretation = VIPS_INTERPRETATION_GREY16; + else + interpretation = VIPS_INTERPRETATION_scRGB; + + g_info( "converting to processing space %s", + vips_enum_nick( VIPS_TYPE_INTERPRETATION, + interpretation ) ); + if( vips_colourspace( in, &t[2], interpretation, + NULL ) ) + return( -1 ); + in = t[2]; + } + } /* Shrink to preshrunk_page_height, so we work for multi-page images. */ @@ -767,7 +778,7 @@ vips_thumbnail_build( VipsObject *object ) /* vips_premultiply() makes a float image. When we * vips_unpremultiply() below, we need to cast back to the - * pre-premultiply format. + * pre-premultiplied format. */ unpremultiplied_format = in->BandFmt; in = t[3]; @@ -779,6 +790,14 @@ vips_thumbnail_build( VipsObject *object ) return( -1 ); in = t[4]; + if( have_premultiplied ) { + g_info( "unpremultiplying alpha" ); + if( vips_unpremultiply( in, &t[5], NULL ) || + vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) ) + return( -1 ); + in = t[6]; + } + /* Only set page-height if we have more than one page, or this could * accidentally turn into an animated image later. */ @@ -794,21 +813,12 @@ vips_thumbnail_build( VipsObject *object ) VIPS_META_PAGE_HEIGHT, output_page_height ); } - if( have_premultiplied ) { - g_info( "unpremultiplying alpha" ); - if( vips_unpremultiply( in, &t[5], NULL ) || - vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) ) - return( -1 ); - in = t[6]; - } - /* Colour management. - * - * If we've already imported, just export. Otherwise, we're in - * device space and we need a combined import/export to transform to - * the target space. */ if( have_imported ) { + /* We've already imported, just export. Go to sRGB if there's + * no export profile. + */ if( thumbnail->export_profile || vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { g_info( "exporting to device space with a profile" ); @@ -827,9 +837,10 @@ vips_thumbnail_build( VipsObject *object ) in = t[7]; } } - else if( thumbnail->export_profile && - (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || - thumbnail->import_profile) ) { + else if( thumbnail->export_profile ) { + /* Not imported, but we are doing colourmanagement. Transform + * to the output space. + */ g_info( "transforming to %s", thumbnail->export_profile ); if( thumbnail->import_profile ) g_info( "fallback input profile %s", @@ -844,6 +855,19 @@ vips_thumbnail_build( VipsObject *object ) return( -1 ); in = t[7]; } + else if( thumbnail->linear ) { + /* Linear mode, no colour management. We went to scRGB for + * precessing, so we now revert to the input + * colourspace. + */ + g_info( "reverting to input space %s", + vips_enum_nick( VIPS_TYPE_INTERPRETATION, + input_interpretation ) ); + if( vips_colourspace( in, &t[7], + input_interpretation, NULL ) ) + return( -1 ); + in = t[7]; + } if( thumbnail->auto_rotate && thumbnail->orientation != 1 ) { From d3ccadf212977c732e8237565e7bc046948a8bb6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 27 Feb 2021 15:16:25 +0000 Subject: [PATCH 02/11] revise unpremultiply, again We were not detecting division by zero carefully enough, nor clipping the alpha range sufficiently in unpremultiply. see https://github.com/libvips/libvips/issues/1941 also see https://github.com/libvips/libvips/pull/1675 for another difficult test case --- ChangeLog | 1 + libvips/conversion/unpremultiply.c | 38 ++++++++++-------------------- libvips/include/vips/util.h | 14 +++++++++++ 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0b6981a0..91c231ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - fix includes of glib headers in C++ [lovell] - fix build with more modern librsvg [lovell] - fix a possible segv with very wide images [f1ac] +- revise premultiply, again [jjonesrs] 18/12/20 started 8.10.5 - fix potential /0 in animated webp load [lovell] diff --git a/libvips/conversion/unpremultiply.c b/libvips/conversion/unpremultiply.c index 219fac28..5efd7696 100644 --- a/libvips/conversion/unpremultiply.c +++ b/libvips/conversion/unpremultiply.c @@ -7,6 +7,8 @@ * - max_alpha defaults to 65535 for RGB16/GREY16 * 24/11/17 lovell * - match normalised alpha to output type + * 27/2/21 jjonesrs + * - revise range clipping and 1/x, again */ /* @@ -78,18 +80,12 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[alpha_band]; \ + IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \ + OUT factor = max_alpha * vips_recip( clip_alpha ); \ \ - if( alpha != 0 ) { \ - OUT factor = max_alpha / alpha; \ - \ - for( i = 0; i < alpha_band; i++ ) \ - q[i] = factor * p[i]; \ - q[alpha_band] = alpha; \ - } \ - else \ - for( i = 0; i < alpha_band + 1; i++ ) \ - q[i] = 0; \ - \ + for( i = 0; i < alpha_band; i++ ) \ + q[i] = factor * p[i]; \ + q[alpha_band] = clip_alpha; \ for( i = alpha_band + 1; i < bands; i++ ) \ q[i] = p[i]; \ \ @@ -106,21 +102,13 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[3]; \ + IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \ + OUT factor = max_alpha * vips_recip( clip_alpha ); \ \ - if( alpha != 0 ) { \ - OUT factor = max_alpha / alpha; \ - \ - q[0] = factor * p[0]; \ - q[1] = factor * p[1]; \ - q[2] = factor * p[2]; \ - q[3] = alpha; \ - } \ - else { \ - q[0] = 0; \ - q[1] = 0; \ - q[2] = 0; \ - q[3] = 0; \ - } \ + q[0] = factor * p[0]; \ + q[1] = factor * p[1]; \ + q[2] = factor * p[2]; \ + q[3] = clip_alpha; \ \ p += 4; \ q += 4; \ diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index feb49f72..2b3e8726 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -84,6 +84,20 @@ extern "C" { #define VIPS_FMIN( A, B ) VIPS_MIN( A, B ) #endif +/* Calculate 1.0 / x, avoiding division by zero when near zero. + */ +static inline double +vips_recip( double x ) +{ + static const double epsilon = 1e-10; + + int sign = x < 0.0 ? -1.0 : 1.0; + + return( sign * x >= epsilon ? + 1.0 / x : + sign / epsilon ); +} + /* Testing status before the function call saves a lot of time. */ #define VIPS_ONCE( ONCE, FUNC, CLIENT ) \ From 0b74acf7ba36a206ba5e3a2a14426c4971bd033b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 27 Feb 2021 15:48:02 +0000 Subject: [PATCH 03/11] add a test for unpremultiply we actually test vipsthumbnail --linear on an RGBA image, which should catch everything, hopefully --- test/test-suite/helpers/helpers.py | 2 ++ test/test-suite/images/rgba-correct.ppm | 5 +++++ test/test-suite/images/rgba.png | Bin 0 -> 21300 bytes test/test-suite/test_resample.py | 8 +++++++- 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/test-suite/images/rgba-correct.ppm create mode 100644 test/test-suite/images/rgba.png diff --git a/test/test-suite/helpers/helpers.py b/test/test-suite/helpers/helpers.py index 585a7f0c..0eee4e7d 100644 --- a/test/test-suite/helpers/helpers.py +++ b/test/test-suite/helpers/helpers.py @@ -42,6 +42,8 @@ BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP") NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz") ICO_FILE = os.path.join(IMAGES, "favicon.ico") HEIC_FILE = os.path.join(IMAGES, "heic-orientation-6.heic") +RGBA_FILE = os.path.join(IMAGES, "rgba.png") +RGBA_CORRECT_FILE = os.path.join(IMAGES, "rgba-correct.ppm") MOSAIC_FILES = [os.path.join(IMAGES, "cd1.1.jpg"), os.path.join(IMAGES, "cd1.2.jpg"), os.path.join(IMAGES, "cd2.1.jpg"), os.path.join(IMAGES, "cd2.2.jpg"), os.path.join(IMAGES, "cd3.1.jpg"), os.path.join(IMAGES, "cd3.2.jpg"), diff --git a/test/test-suite/images/rgba-correct.ppm b/test/test-suite/images/rgba-correct.ppm new file mode 100644 index 00000000..b83fdce9 --- /dev/null +++ b/test/test-suite/images/rgba-correct.ppm @@ -0,0 +1,5 @@ +P6 +#vips2ppm - 2021-02-27T15:39:39.437028Z +64 64 +255 +’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’žžžžžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ééėöö÷’’’’’’’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’¶³¼¾»ĀÉĒĶŻÜąźźģųųł’’’’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’³®µ¶²¹»“½Į»Ć¾¹æĄ»ĆĶĖŃįįäńņóżżż’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’Ŗ¦«¬§­°«²“­³ŗ²¹°§Æŗ“»³°·»¹ĀĖŹŃŽŽāņóō’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’­ŖÆ­©Æؤ©­©®²­“±Ŗ±°«±“®“±¬²µ²ŗø¶¾¹¹ĀŠŠ×čéģüüż’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’Æ©ÆŖ„Ŗ££­Ø­±®²­Ø®§¢Ø²­²ÆŖÆŖ¤Ŗ°«±¬¦®°­²»¹ĀĖĒŃāāēūūü’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’®§­­§­Ŗ„Ŗ«§­ÆŖ²¦Ÿ„«¦­“°¶“Æ“°Ŗ°³­“ÆØ®¢œ ¬¤Ŗ·±¹ŗ¶ĄĒÅĶäåčżżż’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’“®³§¢§Ŗ¦¬®Ŗ°§Ÿ¦©¢Ø©£©«§­«¦­Ŗ£«ÆØ°­§¬”›ŸŖ£Ŗ“­“²«±µ±¶¶³¹ÅĀĖééģ’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’Ŗ¤Ø„Ÿ„©£«¦”§„ž¤®Ø®®ŖÆ£ž£ž—Ŗ„«Ŗ„«ž—¢›”„Ÿ„©¢Ŗ®¦­¤¢©¢§²«²²°øČŹŠńńó’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’³­²°ŖƧ¢©š•›„ ¦±¬²¦”§§”§££­Ŗ®²®“­ŖÆŖ¦ŖŖ„Ŗ¦”§©£Ø¦Ÿ„ž™Ÿ­©­­§¬µ°¹µ³½ÕŌŁüüü’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’«¦«®§¬„ §©£Ŗ¤Ÿ¤™Ÿ¤Ÿ¦Ŗ„Ŗ¤£££©¤Ŗ¬ŖƧ¤Ŗ£ž£©„Ŗ§”§Ŗ£ŖŖ„«§£Ŗ¬§­“¬±­¦«²­“¾¼Äźźģ’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’§”§ÆØ­Ŗ„«§”§Ÿ›Ÿ¢ž£¦¢§žšž£ž£Ŗ¤©§£Ŗœ˜ž¢£”™ „Ÿ¤£ž¤£ž¤¦”§Ø£©Ø£§²«Æ®Ø«”œ ©£Ŗ·³»ÕŌŁśśū’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’§Ÿ§²­²§”§¦”¦ž›Ÿ¢ž£žšžš”šŖ¤Ŗ©£Ø©„«§¢§Ÿ—žØ¢Ø££¦Ÿ¦­Ø°°Ŗ±°Ø®Ÿšžž™””¦£Ŗž›Ÿ§ ¦®©Æŗ¹Įķķļ’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’£œ£„Ÿ¤”› Ø£Ø›•›£Ÿ¤£Ÿ¤š”šØ¢§§”¦ ›¢„”§¤Ÿ„ؤŖ„ ¦Ø Ø¬¤­²­“©£Ø›–›”œ¢”¢”Ÿ„§£Ø£”أثحŖ©°ŪŪą’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’¦ „¢”¢ž£„ž£ŸšŸ¢Ÿ¤­ŖƦŸ£¦ž£®Ø­£ž„ž˜ŸŖ¤Ŗ£ž£©£©›•œ™’˜œš­Ø­ž™«§¬Æ«Æ˜”›£Ÿ„§”¦ž˜”œ ”œŸ¬Ø±ĘĘĪųłś’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’¬¦¬Ÿ›ŸŸ›Ÿ”› „Ÿ„Ÿ› ¬Ø®­¦­«£ØÆØ® ›”£Ÿ¤ÆŖ®Ÿ™œŸ› Ÿ™Ÿš“š›„ ¤Ø£§©£Ø§”§“Ž•¢ž¤§£Ø££¤ž£œ–œ ™ž£ Ø±±¹ģģļ’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’˜§£§§£§–œ˜£Ÿ¤Ø£©¢›”§”§££Ø£©ž™Ÿ¤ž¤¤ž£—›£ž£—™”š££—”–š’— ™Ÿ™Ÿ£ ¦Ŗ¦«¬§¬˜›•šžšž œŸ¢£Ŗ§±āįę’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’”¢¤ ¦„ §”•›•›—œ¤”¤Ÿ›žž˜¢œ”–”˜š”›˜’™ž™Ÿž™ŸšŸŸ› ’Œ’˜”™š•›œ•œ£Ÿ„š–›ž™Ÿ”› ””—“˜ œ”—”˜ŸšŸš”š°¬¶ŻŻć’’’’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’§£§ ›¢—’œž˜Ÿ™”™¤Ÿ£Ŗ¦©£ž”¦Ÿ„££™”š„Ÿ„˜ž‹’›”œ ˜Ÿœ•œŸ™Ÿ£ž¤Ŗ¦¬Ÿ› ”Ž”££Ÿš ˜“˜£›¢˜“˜ ›”˜’š™Ÿ›•›—›”šÆ«¶ŽŻä’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’£Ÿ„¦Ÿ§žšŸ˜“™™“™”œ”§”¦›”š–•ž™ŸŸ›Ÿ«§¬˜•™ž™Ÿ—‘—£›£Ÿš ›ŸŸ›¢§£«™Ÿ—’—™Ÿ„ §›—œ”Ž”›—œ‘‘Ž“£Ÿ„¢£ž–™“˜†Œ­Ø“ąąå’’’żżž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’¤ž„£ž£¦ ¤˜“›˜‘™“”‹›—’“„”§Ø„Ŗž› ”Ž”˜“™ŸšŸ˜’–ž™ž™Ÿ‘Œ‰„ˆ°ŖÆ„ž£——“˜Ÿšž””ŒˆŒ‘Œ›—›–œ”Œ“–Ž”•”Ž‰Ž§„Æźźķ’’’żżż’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’›–œ£ž£—’›œ—Ÿ££š•œ›˜Ÿ™”›—“™•––‘˜Ÿ™”–‘˜Œ’ž™–›„Ÿ£˜“™—›”Ž’™’— šŸœ•›””•“Ÿ›Ÿ“”Ÿ› ™•›Ÿš “Ž”š’˜œ–›™”™”œ ‰‘Ø©±ņóõ’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’”’Œ”š”œŒ”›—ž¦”§¢£–‘˜’Œ“Ÿ™ ž–žŖ”Ŗ’Ž”•”˜œ™ž œ ›–›™žš•›œ—˜’˜˜œ”™Ÿ•–‰…ŠŠ…‹™Ÿ˜”›“”—”™Ÿ› ˜”˜‡„ˆŸ› ›•›“Š‘“—²²½śśū’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ ›Ÿ™‘”£›”ŽˆŽ˜“›žš”‘‰Ž£›Ÿ©£Ŗ˜‘™£œ¢œ˜œ‘Œ“—“™Š†ŒŽ‰„ ¦™”š‘’‹…‹“Š‘˜”˜’‹‘’‹“‰’~xŒ‡‰„‹‹†Œ“Œ’“Ž“š•šŠ…‰›•œ••”Œ““”–•ÅÄĖ’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’Ŗ¤© ›ž›”—Ž‰œ—Ÿ”–ž–œ£¢”Ž“”•–‘˜—”š™”›’”“Ž”“Ž•ž™ŸŖ£Ŗ”›¢–˜‘–•”–‘•›–ž”€|‚„†–’˜“”•”Œ‘Œ‡Œ•‘•‡ŽŽˆ“Œ““Œ’‹’š˜ ŪŪß’’’żżž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’œ–•‘–‹†Š”•„”Ø¢£—’–z€Š‘ ›¢–”™–“™Ÿ›£”–”™Œ‰žš š•›”¢Ÿ˜ž™”™‰…ŠŽ‰ŽŽ‰Ž‰Š…Œ’Œ“Š‘ŽŠ‘Œ‘…€†’“‰‰‘Œ†ƒ}ƒŒ†Œ‡„ˆ¢ Ŗķķļ’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’••‘–ˆ‡ƒ‰‹‘“–•—Š†Œš•œ”•’•”’—~ƒ”“—ŽŠ‘†…‰“‘–Š‡•’–›•›‹†‹—’˜‘‹“˜“›‰…‹““Š†ŒŽ‰ŠŒ”Œ’‘’‡ƒ‰„†’Œ”‰„ŠŠ„‹‘‹‘‹„Šˆƒ‹°®øūūü’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’Ž‰‘Š‘•’—‰…Š’–”––‘—‹‡Ž‰™•›Š†ŒˆŽ‘‰’”Œ“™–—‘–’”™”›“‘–‹‡€}ƒ…Š‘”‘šŠ‘š”™””…†’“”–ˆ„ŠŠ…‹Š†‹„†”–“Ž”‹‘†‹ƒ‰~‚•’œĘĘĶ’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’—˜Žˆ”•‘‹š—œš–œ„ƒ–—”Ž”›—Œ†Œ‹‘ž—ž”˜ž–Œ“œ”œœ–ž‹…‹‘‹’˜‘—”“‡‚‡ˆŽ””™•›€{‚‹‡Ž‰†Œ‹†Œ‹‘xsw‰…Šœ˜žŒ†”–ˆ„…Ž‡Œ…Œ‡‚†Š…Š—˜¢įāå’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’œ–ž“Ž“š”˜’‹•‘”˜“˜“Šœ”›š”›™”œ›—Ÿ”—”‘—Š‡€ˆš’šŸ˜Ÿ–•”Œ“˜‘—˜’——’•„€†˜”™‹†Œxrzyt}ŒˆŽ‡‚ˆ‘Œ‘…€†ƒ€ƒ’ŒˆŽ‰‡„‚ˆ{z€€z€‡‚††€„‡ƒ‹««³÷÷ł’’’žžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ˆ„ŠŽ‰Žš”š‡ŒŠ¢œ”›“—‡~ƒ‡…‚ˆŒŠ‘Ž‹‘ˆ„‰‘Œ“‰Ž‡”Œ”›”œ‰„Š†‚‰“”Ž‹…‚ˆ‘‰—‘—ˆƒŠˆƒ‹‚~ƒ…€…ŽˆŽŠ‘„ƒ‚~‚}ƒwt{~„€~„}‚…†ƒ~ƒ‹…Œz‰“ĘÅĪ’’’žžžžžžžžžžžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’”Ž”–ž¢œ¤‡‚ˆ‹‡›–›““€x~”“’Ž”‘•‘”’Œ“‘Š‰„‰€z€Œ†Ž‡Ž‡Ž‹“„…Œ‰…‡’‰‘”Ž”ŠƒŠ™“˜ˆŽ‰„Š‰ŽŠ‹ƒ„~z€‚~„zv}ƒ€‡‘•‹ˆ‚}„„€‡{‚„…£ «āćč’’’’’’’’’’’’’’’’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’’’’’’’’’’ŽŠ‘“—•’™ŽŠ‰‡‹‡˜“™‡‚ˆ‰„‰Š…‹–”˜‘“Œ‡Œ‰ƒˆŒ‡‹›˜ŒˆŒŽ†˜“™{x{{~…‚‰‰…‹–‘•…€‡…€ˆ‘‹‘–‘–‹’‚ƒ~yŠ†Œ€|‚€z}xzv~†ƒŠ‡„‹„‡|ƒ…€‡}y{€‰„Œ©§±ģģńõõųšļņššņóóōüüü’’’’’’’’’’’’’’’žžžžžž’’’’’’’’’’’’’’’’’’{zƒ„€ˆŠˆ‹“‹ŠŽŒ“Œ†Ž‡‚ˆ‹‰…ƒ‡‚|ŽŠ‰†‹€}ƒŠ…}zƒ””‘Œ“†‡xtx‡ƒˆ‹‡Ž…€‡ˆŽ‹…Œ…†y€ŽˆŽ“”sosxsx‰ƒŠˆ‚‰yu{…€†‚}ƒ‚}ƒ…€‡‹†ŒŠ†Œˆ„‰…€……}‚‡~…ŒƒÅĆĪééóčęļéēīźčīšīóéēķåäéīķņļīšųųų’’’’’’žžžžžž’’’’’’’’’’’’Œ”Š††‚ˆŒ‡–“™Œ‘ˆ†Œyw}‡„‰Š‡ˆ‰z‚‚…‰„‹…‚‰ur{‚~„‹‡‡‚ˆš”š‰…Š}y„†‹‘”˜“Ž–€z~w~‹†Œzwsyƒ~„‰„‹ˆƒ‹„}…‰ƒ‹y€†€‡ˆƒ‰‡ƒˆ††‡€†ˆ‡ŒƒŠ…Ž œØēęńńļųóš÷õółöōūöōūöóūōńśķźóćąéźčķõõö’’’’’’žžž’’’’’’’’’•’—‰†Œˆ…ŠŒ†ˆ‡˜’—~‚{z~Š‰ˆ‚‰ˆ„ŠŒ‰““ˆˆ‰|ƒƒ…‘’”Ž”•”…}„}w’ŽŠ‘€|ƒ‚…‚}ƒ‹…‹†ˆƒŠ{v|{v}€{‚‡‰‡‰Š…Œ}w~„~…vov‚z€‰‚ˆz•–™”§ ÆąŻźģźņōń÷óš÷šīöńīöńīöīģõģéóšģöōńłįŽēįąå÷÷÷’’’žžžžžž’’’|Œ‡Ž’Ž”€|ƒ‚~…’”Š…‰‰†‰‹…ŒŒˆŽ‚ƒuty†„Š’Š‡|…‡~ˆ‹…€z‚††xrv‹…‹Ž†‚~…ƒ†‹†Ž€y€Œ†Œx~††‡ƒˆƒ}ƒzv|xty|v}ˆ‚‰ƒ~„”•…„rlrysxƒ}‚x€…}†•š¹“æßŪęīģõšķõņńųńīöņļ÷ņš÷ļģōģéņģéņėčńźęļęįėćßźŻŪćīīš’’’’’’žžž‘Ž“xsy‰Žƒ„|z~Š†‚}ƒ…€ˆˆ‚|x‚‚„€~„ˆ†Œ…‹‘‰€}‚‚}„Š„‹†‚}€‰„‰’•‡‚‡ˆ„‰Ž‰‘mfn|v|ˆ‚ˆ‰„Šzw{~x~}y~ƒ}‚~w~{uzw}Š„Š„~„„}‚~x~tms…~…ššĖÄŠėčńöōśóńųņļ÷ņš÷ńīöšīöńīöīģōģčńīėóéęīęćģćŻčęāėčęļŚŲćāįå’’’’’’‰…‹Œ’Ž‹Ž”’}w|ƒ~„ws|y‚Šƒ‹‹…‡ƒ‰|‚‚…‚„tnsqps~zˆ‰‚‰|w|wqwƒ†~x}}vzw}}uz|wzx„~„ƒ„zv||w}}x}z†‡umstnu~xy€y‚——¹°»ŲÓŻńīųõóśõņųņļ÷ņļ÷ōņłņļ÷īėōļķõļķõģéóčåļēćķāŻčāŻēēćķāŽčÓĻŚÕŃÜŲׯśūś‚~ƒ•‘—y}‡ˆ†‚ˆvrx~ƒ€|€ˆƒ‹€}…xr{zv|‚~ƒ……yvzxu{yv}{v}x~|v}}x€””€yˆ€†Œ‡Œ‰ƒ‡’‹‡ƒ‡xsxvqv|uz}u{wpuwqwqjqmgm™˜»“¾ČĮĶÜÕāģēņöóūöōśōóųōņųóńųšīöšķöńīöļķõļīöķźņęćķéåšģčńąŪęåąėćßźŲÕßĪŹÕÖŃÜÖŃŪŁŲŽƒ‡„€‡…‡‡†ˆ‡Ž‹…Œˆ„Š€|‚„€†ƒ…ˆ}x‰…‰•”|w}yu||x~xsxŽˆŒtpv…†”Ž”†€…ˆˆ‡Ž€y‹„‹‰Ž{wz…~ƒŒ„‹ƒ|nhnqin–‘˜·³»ŅĢÖŲÓßåąėńī÷óšųóńųõółšīöšīöņš÷īģōļėōńīöčäīēćīéēļäąėäßźčäīÜ×湌åŪÕߊŹÕ×ŌŽŪ×āĻČÓĢÉՄ†“•‹†|Š‰…‹~z€|ƒ}…‰„Œ‹Ž€z„€‡‚€†…‚†Š‘y‚…†‚}ƒtqvrpw~y€“Ž’‹…‹|v}‚}„„…vow}ƒ€{umstmux– Ą·ĀŌĪŲ×ÓŽßÜčķźōõōśōółńļöóńųņš÷ļģõļīöļķõļėōėēšéćīēāķźęļźēńćßźāŽéÜŲäŅĖ×ßŪęßŪꊏÕŪ×āŅĪŁ¼¶Į¼¶Ć‡ƒ‰––‡ƒ‰†ƒˆ„~„z€}}wqjs‹‡ˆ„‹|x€€}ƒ}yˆ†‹‚~ƒ}xˆƒŠ‡ˆ}xvsxysxxryyt{wrxyt{rnvyu~qls{s{™’šĖÄĪŪÕąćŻéēćīīėōōņłõóśņļ÷šīöņļ÷óņłńīöļģōńīöķéņēäīėēńčćīģčņ掟Ż×äÜ×ćąÜēŪ×āŚÕąŽŚåŚ×āÉÄŠŹĘŅŗ¶Ā°Ŗø¹“Ā†ƒ‰„€„ƒ‡‚}†~yzv~Œ†z~w|„~„†„‡}„‚}„Œˆ‰„‰‰†‹wrwpkr~x€ˆ‚‰„…xsytmt…~…€zyvyysw‹„Š‚y€ —žČĀŹäŽčåąėčäīóńłöōūóņųōņųóņųńīöńīöļģõšķöńīöķéóķéōķźóēäīéåļéåļßŪēŻŲåŽŚę掟äßźßŪꎌåŠĖÖľŹ¼“ĀĒĮĪÄĄĶĄ¼ĖÉÅŅƒ„}x‡ƒ‹…ˆ|w~y‚†‡Œ…Š’‡’‰ƒ}xuyxqz†ƒˆ‰†‹„‚‰„~„€xxsy‚z‚ƒ}ƒ~xz‚‰ƒ‰‡…‰Œˆ£˜Ÿæ¶¾ŁŅŚåßčéćķńīöóńłöōūōņųņļöóńųńļ÷ļģõńīöļģōšķöļģõķźóķéóėēņźēńźęšßŚęąŪč×ŌßąŻčäßģŻŚåÕŠŪĪÉŌČĆĪĀ¾ŹĄŗÅ·±¾ÄæĢŗ“Į½¹ĒĮ¾Ė˜”šws|Š‡Šˆvrxƒ††‚‰zu}{v}„~„ƒ}ƒ}ƒzw~tqx‡ƒŠ‹ˆŽ|sy~u|‰€ˆ…Žzvmrshn‹‚ˆ“­µŹÄĶŽÖąäßźōńłņšųōšųóš÷ōņųņļöóļ÷ņīöīźņšķõšķõīģōėēóźęńéęļåąėźēńėčńŽŚę掟ŽŚåąÜéåąģąÜ莌ęĶČÕÅĄĪĻŹÖĪÉÕĄ»ÉĀ¾ĖĒĮĪÄĄĢ”œŖŖ§“Ŗ§“{vqow~|‚€{|w~~x~†‡tqx|w~‡‰‚}†qoujinxu|‚‡~y‚wrww‡~†„|ƒˆ~†§ §Ā¼ĀŲŅÜŽŁćźåļļķöņšųōņųōńųńīöńļöņšųšīöķźóšķõļģõīźņģēšéåļēäļėēńėēńčäīęįģÜÖāŚÕįäßģāŻéąŻčŻŁåŃĢŲĶČÖĘĮŠĖĘŌÖŃŻĻŹ×ŹÅŅĶĒŌÄĄĶ¤£²œ›Ŗ¦£²§£°…†ƒ†zƒxqw‚|‚ƒ~„x…}„†€†ytz~x€…€†~y€|x}wrx„€…limƒ}„„~…ÆØÆęąéģźōäßģīźõšīöõóśóńųņš÷šīöóņųšīöšķõńīöģéņīėōėčńģéņģčńćŻčėēńéęļęāķāßźŚÖāŽŲåŚŅŽßŪęāŽź×ŅŻŌĪŁĒĀĪľĖĢĒŌĶŹ×ŠĢŁĖČÓ²­ŗ¹³ĄĆæĖØ¢±œšØœ›«Ŗض©§µ„~ƒ€zrjs~w…€„y}w~ˆ€ˆzƒplq~x}€x~ˆ…‹onsrlq~v|—Š”ČĮĖÖŅÜčäļķźōņšųīėõņļųōņłóń÷ńļöšīöńīöņš÷ņļųėčńėēńīģōźēšćßźėēšéåīßŪåęäīäāģßŪęŻŪęßÜēäąģąÜčŪÖćÓĪŪČĀĶĻŹÕŹÄŃŠĖŁŌŃŽČĒÓŹČÕ»¶Ä”›Ŗø“Ā«©µ‘Œ›„”ÆŖ¦“Ŗ§“››Ŗ}u}‹„Š†€…~x…‡wsw„…x€‡€‡„€…}v|{s{ysy†Žø±¹ĪČŃ׊ÜāŻźŻ×åīėõóńųöōśõółńļöņļųōóśńļöīźóņš÷īģõėēńļīõėéņģčóīėóęāķčåļįŻéŽŚåčęļŻŚåŁÕąÜ×äßŪēŁ×įĪŹÕŹĀŠÅĄĪÅĮĻŠĢŲĻĖ×ĪŹ×½¼ĒŖ©¶¦„³§¤²­Ø·²®½£Ÿ­ž›Ŗ§¢°ÆŖø”œ«›˜Ø„‡ŽˆŽ€y~umu†€ˆoin}w}~xuqwjekŽ†š”™Į¹Ąęąéķčńņīųźčóņšųōņśóńųōółōņłšīöļģõóńłńļ÷ļķõļķöļķöļīöźęńźęšäąźäßėąÜēŻŲäÜŚęŌĻÜŁŌßŌĻŚĪĒÓÖŠÜŅĢŲŹÄŃÄæĖČĀĻĪČÕČÄŃĒĆŃĖČÕ¹µĀ­§µ¢ž­£ŸÆ”­›©¦£±¢ž­˜•¦£ °£ Æ››©—•£œŖŒ†‰„Šuns|t|}urjsgbj~y~•—Æ«²ŚÕŽęāėóļų÷öżņšųõņłõółōółōņųńļöšīöņšųļģõńļ÷ņšųšīöėčóėéóģźōāŽźäąģŪŲåŽŪęęāķęāģäąźćąģÕŅŽŽÜēÕŅŻŃĢ×ÖŅŽÖŅŽŃĶŚĢČÖĪĖŁĖČÕČĘŅÄĀĻ»¹Ē­Ø¶Ŗ¤µŖ§¶­Ŗŗ°­¾²Æ½¦£±–”¤›«¤ °–”””“ ”“ŸŖ‚}…ˆ„‰yvxxuyqls~w|§£ØŹÅĢćŽčćßģģéóöóūńī÷ōółōółņš÷ļīöņšųōņłļīöļīöóńųīģõķėōķėōčäļźčņäįģėčņęāķęćīźēņęćķćßėŚÕßŌĪŚĪĒŌĶČŌŌŃÜŹÅÓŠĖ×ŌŃŻŹĒŅĶŹÕĒĆŠĒĀŠ½¹ĘÆ­¹Ø¤²”ž­Ŗ¦“ø³Ć±®¾Æ­½©§¶Ø¦³ ž­”“”Ÿœ¬ ž­˜•£–”¢˜–£š›§†‰pjp{u|‘Š·±øŁŌÜčäīéåļšī÷ģéņņš÷õōśņńųņńųóńłšīöńļöńī÷ļķöšī÷ėéóļīöķėōźēńīģöėźóģźóéēńęāķęāīßŪčćąėßŪēĶČÕĻŹ×ĻÉÖĪČÕÖŅŻŲŌßĘĄĢĆ½ŹĄ½É³°½µ²æ®§³Ŗ£±ŸœŖ¤”ƤŸ®¤”°©§“¬Øø”ÆŸœ­”“”™˜¦””£š™§¢ Æœš©›š§ Ÿ­›Ŗ£¤² \ No newline at end of file diff --git a/test/test-suite/images/rgba.png b/test/test-suite/images/rgba.png new file mode 100644 index 0000000000000000000000000000000000000000..50fd86f273a671e5e5474f04e8a6e7e37391dbc1 GIT binary patch literal 21300 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 literal 0 HcmV?d00001 diff --git a/test/test-suite/test_resample.py b/test/test-suite/test_resample.py index afd21422..ea376e90 100644 --- a/test/test-suite/test_resample.py +++ b/test/test-suite/test_resample.py @@ -2,7 +2,8 @@ import pytest import pyvips -from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, have +from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, \ + have, RGBA_FILE, RGBA_CORRECT_FILE # Run a function expecting a complex image on a two-band image @@ -193,6 +194,11 @@ class TestResample: im2 = pyvips.Image.thumbnail_buffer(buf, 100) assert abs(im1.avg() - im2.avg()) < 1 + # linear shrink should work on rgba images + im1 = pyvips.Image.thumbnail(RGBA_FILE, 64, linear=True) + im2 = pyvips.Image.new_from_file(RGBA_CORRECT_FILE) + assert abs(im1.flatten(background=255).avg() - im2.avg()) < 1 + if have("heifload"): # this image is orientation 6 ... thumbnail should flip it im = pyvips.Image.new_from_file(HEIC_FILE) From 303513e52d7a4bce7d338813646b270e693211a0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 27 Feb 2021 16:00:33 +0000 Subject: [PATCH 04/11] small comment polish --- ChangeLog | 1 + libvips/resample/thumbnail.c | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 91c231ba..3db8a72f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ - fix build with more modern librsvg [lovell] - fix a possible segv with very wide images [f1ac] - revise premultiply, again [jjonesrs] +- revise profile handling in vipsthumbnail 18/12/20 started 8.10.5 - fix potential /0 in animated webp load [lovell] diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 6db6950f..1a046e1a 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -694,12 +694,12 @@ vips_thumbnail_build( VipsObject *object ) /* In linear mode, we need to transform to a linear space before * vips_resize(). - * - * If we are doing colour management (there's an import profile), - * then we use XYZ PCS as the resize space. */ have_imported = FALSE; if( thumbnail->linear ) { + /* If we are doing colour management (there's an import + * profile), then we can use XYZ PCS as the resize space. + */ if( in->Coding == VIPS_CODING_NONE && (in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT) && @@ -717,7 +717,6 @@ vips_thumbnail_build( VipsObject *object ) "pcs", VIPS_PCS_XYZ, NULL ) ) return( -1 ); - in = t[1]; have_imported = TRUE; @@ -771,16 +770,16 @@ vips_thumbnail_build( VipsObject *object ) if( vips_image_hasalpha( in ) && hshrink != 1.0 && vshrink != 1.0 ) { - g_info( "premultiplying alpha" ); - if( vips_premultiply( in, &t[3], NULL ) ) - return( -1 ); - have_premultiplied = TRUE; - /* vips_premultiply() makes a float image. When we * vips_unpremultiply() below, we need to cast back to the * pre-premultiplied format. */ + g_info( "premultiplying alpha" ); + have_premultiplied = TRUE; unpremultiplied_format = in->BandFmt; + + if( vips_premultiply( in, &t[3], NULL ) ) + return( -1 ); in = t[3]; } @@ -838,7 +837,7 @@ vips_thumbnail_build( VipsObject *object ) } } else if( thumbnail->export_profile ) { - /* Not imported, but we are doing colourmanagement. Transform + /* Not imported, but we are doing colour management. Transform * to the output space. */ g_info( "transforming to %s", thumbnail->export_profile ); From e1fc576252722cf1721cc1573a59ebf20ea3b2a0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 27 Feb 2021 16:34:50 +0000 Subject: [PATCH 05/11] back to srgb/b-w processing space for compat in thumbnail behaviour --- libvips/resample/thumbnail.c | 37 +++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 1a046e1a..ac17c8eb 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -30,6 +30,9 @@ * - add thumbnail_source * 2/6/20 * - add subifd pyr support + * 27/2/21 + * - simplify rules re. processing space, colour management and linear + * mode */ /* @@ -673,7 +676,7 @@ vips_thumbnail_build( VipsObject *object ) */ preshrunk_page_height = vips_image_get_page_height( in ); - /* Coded forms need special unpacking. + /* RAD needs special unpacking. */ if( in->Coding == VIPS_CODING_RAD ) { g_info( "unpacking Rad to float" ); @@ -684,13 +687,6 @@ vips_thumbnail_build( VipsObject *object ) return( -1 ); in = t[12]; } - else if( in->Coding == VIPS_CODING_LABQ ) { - g_info( "unpacking LABQ to float" ); - - if( vips_LabQ2Lab( in, &t[12], NULL ) ) - return( -1 ); - in = t[12]; - } /* In linear mode, we need to transform to a linear space before * vips_resize(). @@ -745,6 +741,25 @@ vips_thumbnail_build( VipsObject *object ) in = t[2]; } } + else { + /* In non-linear mode, use sRGB or B_W as the processing + * space. + */ + VipsInterpretation interpretation; + + if( in->Bands < 3 ) + interpretation = VIPS_INTERPRETATION_B_W; + else + interpretation = VIPS_INTERPRETATION_sRGB; + + g_info( "converting to processing space %s", + vips_enum_nick( VIPS_TYPE_INTERPRETATION, + interpretation ) ); + if( vips_colourspace( in, &t[2], interpretation, + NULL ) ) + return( -1 ); + in = t[2]; + } /* Shrink to preshrunk_page_height, so we work for multi-page images. */ @@ -1393,7 +1408,7 @@ vips_thumbnail_buffer_init( VipsThumbnailBuffer *buffer ) * * @intent: #VipsIntent, rendering intent * * @option_string: %gchararray, extra loader options * - * Exacty as vips_thumbnail(), but read from a memory buffer. One extra + * Exactly as vips_thumbnail(), but read from a memory buffer. One extra * optional argument, @option_string, lets you pass options to the underlying * loader. * @@ -1589,7 +1604,7 @@ vips_thumbnail_source_init( VipsThumbnailSource *source ) * * @intent: #VipsIntent, rendering intent * * @option_string: %gchararray, extra loader options * - * Exacty as vips_thumbnail(), but read from a source. One extra + * Exactly as vips_thumbnail(), but read from a source. One extra * optional argument, @option_string, lets you pass options to the underlying * loader. * @@ -1696,7 +1711,7 @@ vips_thumbnail_image_init( VipsThumbnailImage *image ) * * @export_profile: %gchararray, export ICC profile * * @intent: #VipsIntent, rendering intent * - * Exacty as vips_thumbnail(), but read from an existing image. + * Exactly as vips_thumbnail(), but read from an existing image. * * This operation * is not able to exploit shrink-on-load features of image load libraries, so From 0407c365d109c8f736fb2b5cb4b2ada11b2da912 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 27 Feb 2021 20:47:29 +0000 Subject: [PATCH 06/11] better again --- libvips/conversion/unpremultiply.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libvips/conversion/unpremultiply.c b/libvips/conversion/unpremultiply.c index 5efd7696..780eeaa5 100644 --- a/libvips/conversion/unpremultiply.c +++ b/libvips/conversion/unpremultiply.c @@ -72,7 +72,8 @@ typedef VipsConversionClass VipsUnpremultiplyClass; G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); -/* Unpremultiply an N-band image. +/* Unpremultiply an N-band image. Don't use clip_alpha to calculate factor: we + * want over and undershoots on alpha and RGB to cancel. */ #define UNPRE_MANY( IN, OUT ) { \ IN * restrict p = (IN *) in; \ @@ -80,12 +81,11 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[alpha_band]; \ - IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \ - OUT factor = max_alpha * vips_recip( clip_alpha ); \ + OUT factor = max_alpha * vips_recip( alpha ); \ \ for( i = 0; i < alpha_band; i++ ) \ q[i] = factor * p[i]; \ - q[alpha_band] = clip_alpha; \ + q[alpha_band] = VIPS_CLIP( 0, alpha, max_alpha ); \ for( i = alpha_band + 1; i < bands; i++ ) \ q[i] = p[i]; \ \ @@ -102,13 +102,12 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[3]; \ - IN clip_alpha = VIPS_CLIP( 0.0, alpha, max_alpha ); \ - OUT factor = max_alpha * vips_recip( clip_alpha ); \ + OUT factor = max_alpha * vips_recip( alpha ); \ \ q[0] = factor * p[0]; \ q[1] = factor * p[1]; \ q[2] = factor * p[2]; \ - q[3] = clip_alpha; \ + q[3] = VIPS_CLIP( 0, alpha, max_alpha ); \ \ p += 4; \ q += 4; \ From ea2264ea2eb7c1c80a32bde7d0e57c7b7a4178fd Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 5 Mar 2021 10:44:31 +0000 Subject: [PATCH 07/11] Update libvips/resample/thumbnail.c Co-authored-by: Kleis Auke Wolthuizen --- libvips/resample/thumbnail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index ac17c8eb..fb0a85c0 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -871,7 +871,7 @@ vips_thumbnail_build( VipsObject *object ) } else if( thumbnail->linear ) { /* Linear mode, no colour management. We went to scRGB for - * precessing, so we now revert to the input + * processing, so we now revert to the input * colourspace. */ g_info( "reverting to input space %s", From ef4f95cddc9aa054da184cf39f2d2aabfdc49007 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 5 Mar 2021 10:46:06 +0000 Subject: [PATCH 08/11] cleanup for Kleis comments --- libvips/resample/thumbnail.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index ac17c8eb..5874105f 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -640,9 +640,8 @@ vips_thumbnail_build( VipsObject *object ) */ VipsInterpretation input_interpretation; - /* TRUE if we've premultiplied and need to unpremultiply. + /* The format we need to revert to after unpremultiply. */ - gboolean have_premultiplied; VipsBandFormat unpremultiplied_format; #ifdef DEBUG @@ -778,19 +777,19 @@ vips_thumbnail_build( VipsObject *object ) vshrink = (double) in->Ysize / target_image_height; } + /* vips_premultiply() makes a float image, so when we unpremultiply + * below we must cast back to the original format. Use NOTSET to + * meran no pre/unmultiply. + */ + unpremultiplied_format = VIPS_FORMAT_NOTSET; + /* If there's an alpha, we have to premultiply before shrinking. See * https://github.com/libvips/libvips/issues/291 */ - have_premultiplied = FALSE; if( vips_image_hasalpha( in ) && hshrink != 1.0 && vshrink != 1.0 ) { - /* vips_premultiply() makes a float image. When we - * vips_unpremultiply() below, we need to cast back to the - * pre-premultiplied format. - */ g_info( "premultiplying alpha" ); - have_premultiplied = TRUE; unpremultiplied_format = in->BandFmt; if( vips_premultiply( in, &t[3], NULL ) ) @@ -804,7 +803,7 @@ vips_thumbnail_build( VipsObject *object ) return( -1 ); in = t[4]; - if( have_premultiplied ) { + if( unpremultiplied_format != VIPS_FORMAT_NOTSET ) { g_info( "unpremultiplying alpha" ); if( vips_unpremultiply( in, &t[5], NULL ) || vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) ) From 9585feb5a8a594513826244b81b2d3c59ef51142 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 5 Mar 2021 11:11:49 +0000 Subject: [PATCH 09/11] allow thumbnail colourspace export with no import allow this case: vipsthumbnail k2.jpg --export-profile cmyk for an image with no embedded profile --- libvips/resample/thumbnail.c | 44 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 799ce732..f0f20fe8 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -855,23 +855,41 @@ vips_thumbnail_build( VipsObject *object ) * to the output space. */ g_info( "transforming to %s", thumbnail->export_profile ); - if( thumbnail->import_profile ) - g_info( "fallback input profile %s", - thumbnail->import_profile ); - if( vips_icc_transform( in, &t[7], - thumbnail->export_profile, - "input_profile", thumbnail->import_profile, - "intent", thumbnail->intent, - "embedded", TRUE, - NULL ) ) - return( -1 ); - in = t[7]; + /* If there's some kind of import profile, we can transform to + * the output. Otherwise we have to convert to PCS and then + * export. + */ + if( thumbnail->import_profile || + (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || + thumbnail->import_profile) ) { + g_info( "transforming with supplied profiles" ); + if( vips_icc_transform( in, &t[7], + thumbnail->export_profile, + "input_profile", thumbnail->import_profile, + "intent", thumbnail->intent, + "embedded", TRUE, + NULL ) ) + return( -1 ); + + in = t[7]; + } + else { + g_info( "exporting with %s", + thumbnail->export_profile ); + if( vips_colourspace( in, &t[7], + VIPS_INTERPRETATION_XYZ, NULL ) || + vips_icc_export( t[7], &t[8], + "output_profile", thumbnail->export_profile, + "intent", thumbnail->intent, + NULL ) ) + return( -1 ); + in = t[8]; + } } else if( thumbnail->linear ) { /* Linear mode, no colour management. We went to scRGB for - * processing, so we now revert to the input - * colourspace. + * processing, so we now revert to the input colourspace. */ g_info( "reverting to input space %s", vips_enum_nick( VIPS_TYPE_INTERPRETATION, From 726fded66c272b42daa6a268c8f97023f7ae6105 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 5 Mar 2021 11:24:00 +0000 Subject: [PATCH 10/11] formatting --- libvips/resample/thumbnail.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index f0f20fe8..40826a43 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -879,10 +879,11 @@ vips_thumbnail_build( VipsObject *object ) thumbnail->export_profile ); if( vips_colourspace( in, &t[7], VIPS_INTERPRETATION_XYZ, NULL ) || - vips_icc_export( t[7], &t[8], - "output_profile", thumbnail->export_profile, - "intent", thumbnail->intent, - NULL ) ) + vips_icc_export( t[7], &t[8], + "output_profile", + thumbnail->export_profile, + "intent", thumbnail->intent, + NULL ) ) return( -1 ); in = t[8]; } From 0623a05f48baf9026dbb261203b8f892a4c326dc Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 5 Mar 2021 20:19:05 +0000 Subject: [PATCH 11/11] remove vips_recip() it wasn't really necessary, and it was rather slow --- libvips/conversion/unpremultiply.c | 4 ++-- libvips/include/vips/util.h | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/libvips/conversion/unpremultiply.c b/libvips/conversion/unpremultiply.c index 780eeaa5..88bbbed2 100644 --- a/libvips/conversion/unpremultiply.c +++ b/libvips/conversion/unpremultiply.c @@ -81,7 +81,7 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[alpha_band]; \ - OUT factor = max_alpha * vips_recip( alpha ); \ + OUT factor = alpha == 0 ? 0 : max_alpha / alpha; \ \ for( i = 0; i < alpha_band; i++ ) \ q[i] = factor * p[i]; \ @@ -102,7 +102,7 @@ G_DEFINE_TYPE( VipsUnpremultiply, vips_unpremultiply, VIPS_TYPE_CONVERSION ); \ for( x = 0; x < width; x++ ) { \ IN alpha = p[3]; \ - OUT factor = max_alpha * vips_recip( alpha ); \ + OUT factor = alpha == 0 ? 0 : max_alpha / alpha; \ \ q[0] = factor * p[0]; \ q[1] = factor * p[1]; \ diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index 2b3e8726..feb49f72 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -84,20 +84,6 @@ extern "C" { #define VIPS_FMIN( A, B ) VIPS_MIN( A, B ) #endif -/* Calculate 1.0 / x, avoiding division by zero when near zero. - */ -static inline double -vips_recip( double x ) -{ - static const double epsilon = 1e-10; - - int sign = x < 0.0 ? -1.0 : 1.0; - - return( sign * x >= epsilon ? - 1.0 / x : - sign / epsilon ); -} - /* Testing status before the function call saves a lot of time. */ #define VIPS_ONCE( ONCE, FUNC, CLIENT ) \