add HDR support to heif load/save (#2596)
* heifload done, doing save * finish save, add tests, docs
This commit is contained in:
parent
0388e54bd2
commit
e985e23c09
@ -1,6 +1,7 @@
|
||||
21/11/21 started 8.13
|
||||
- configure fails for requested but unmet dependencies [remicollet]
|
||||
- add support for another quantiser [DarthSim]
|
||||
- add support for HDR HEIC and AVIF images
|
||||
- add spngsave
|
||||
- jpeg2000 load left-justifies bitdepth
|
||||
- add "password" option to pdfload
|
||||
|
@ -2135,6 +2135,9 @@ vips_foreign_find_save_buffer( const char *name )
|
||||
* If @thumbnail is %TRUE, then fetch a stored thumbnail rather than the
|
||||
* image.
|
||||
*
|
||||
* The bitdepth of the heic image is recorded in the metadata item
|
||||
* `heif-bitdepth`.
|
||||
*
|
||||
* See also: vips_image_new_from_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
@ -2235,6 +2238,7 @@ vips_heifload_source( VipsSource *source, VipsImage **out, ... )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @effort: %gint, encoding effort
|
||||
@ -2257,6 +2261,9 @@ vips_heifload_source( VipsSource *source, VipsImage **out, ... )
|
||||
* Chroma subsampling is normally automatically disabled for Q >= 90. You can
|
||||
* force the subsampling mode with @subsample_mode.
|
||||
*
|
||||
* Use @bitdepth to set the bitdepth of the output file. HEIC supports at
|
||||
* least 8, 10 and 12 bits; other codecs may support more or fewer options.
|
||||
*
|
||||
* See also: vips_image_write_to_file(), vips_heifload().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
@ -2284,6 +2291,7 @@ vips_heifsave( VipsImage *in, const char *filename, ... )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @effort: %gint, encoding effort
|
||||
@ -2336,6 +2344,7 @@ vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @Q: %gint, quality factor
|
||||
* * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits
|
||||
* * @lossless: %gboolean, enable lossless encoding
|
||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||
* * @effort: %gint, encoding effort
|
||||
|
@ -22,6 +22,8 @@
|
||||
* - block broken thumbnails, if we can
|
||||
* 14/2/21 kleisauke
|
||||
* - move GObject part to heif2vips.c
|
||||
* 22/12/21
|
||||
* - add >8 bit support
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -159,6 +161,10 @@ typedef struct _VipsForeignLoadHeif {
|
||||
int page_width;
|
||||
int page_height;
|
||||
|
||||
/* Eg. 8 or 12, typically.
|
||||
*/
|
||||
int bits_per_pixel;
|
||||
|
||||
/* The page number currently in @handle.
|
||||
*/
|
||||
int page_no;
|
||||
@ -453,6 +459,8 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
|
||||
int n_metadata;
|
||||
struct heif_error error;
|
||||
VipsForeignHeifCompression compression;
|
||||
VipsInterpretation interpretation;
|
||||
VipsBandFormat format;
|
||||
|
||||
/* We take the metadata from the non-thumbnail first page. HEIC
|
||||
* thumbnails don't have metadata.
|
||||
@ -462,7 +470,8 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
|
||||
|
||||
/* Verify dimensions
|
||||
*/
|
||||
if ( heif->page_width < 1 || heif->page_height < 1 ) {
|
||||
if( heif->page_width < 1 ||
|
||||
heif->page_height < 1 ) {
|
||||
vips_error( "heifload", "%s", _( "bad dimensions" ) );
|
||||
return( -1 );
|
||||
}
|
||||
@ -474,6 +483,11 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
|
||||
#endif /*DEBUG*/
|
||||
bands = heif->has_alpha ? 4 : 3;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "heif_image_handle_get_luma_bits_per_pixel() = %d\n",
|
||||
heif_image_handle_get_luma_bits_per_pixel( heif->handle ) );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* FIXME .. IPTC as well?
|
||||
*/
|
||||
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs(
|
||||
@ -637,6 +651,17 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
|
||||
vips_enum_nick( VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
||||
compression ) );
|
||||
|
||||
vips_image_set_int( out, "heif-bitdepth", heif->bits_per_pixel );
|
||||
|
||||
if( heif->bits_per_pixel > 8 ) {
|
||||
interpretation = VIPS_INTERPRETATION_RGB16;
|
||||
format = VIPS_FORMAT_USHORT;
|
||||
}
|
||||
else {
|
||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||
format = VIPS_FORMAT_UCHAR;
|
||||
}
|
||||
|
||||
/* FIXME .. we always decode to RGB in generate. We should check for
|
||||
* all grey images, perhaps.
|
||||
*/
|
||||
@ -644,7 +669,7 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
|
||||
return( -1 );
|
||||
vips_image_init_fields( out,
|
||||
heif->page_width, heif->page_height * heif->n, bands,
|
||||
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB,
|
||||
format, VIPS_CODING_NONE, interpretation,
|
||||
1.0, 1.0 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename,
|
||||
@ -733,6 +758,10 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
|
||||
heif_image_handle_get_width( thumb_handle ) );
|
||||
printf( " height = %d\n",
|
||||
heif_image_handle_get_height( thumb_handle ) );
|
||||
printf( " bits_per_pixel = %d\n",
|
||||
heif_image_handle_get_luma_bits_per_pixel(
|
||||
thumb_handle ) );
|
||||
|
||||
}
|
||||
}
|
||||
#endif /*DEBUG*/
|
||||
@ -744,6 +773,14 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
|
||||
return( -1 );
|
||||
heif->page_width = heif_image_handle_get_width( heif->handle );
|
||||
heif->page_height = heif_image_handle_get_height( heif->handle );
|
||||
heif->bits_per_pixel =
|
||||
heif_image_handle_get_luma_bits_per_pixel( heif->handle );
|
||||
if( heif->bits_per_pixel < 0 ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "undefined bits per pixel" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
for( i = heif->page + 1; i < heif->page + heif->n; i++ ) {
|
||||
if( vips_foreign_load_heif_set_page( heif,
|
||||
i, heif->thumbnail ) )
|
||||
@ -751,7 +788,10 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
|
||||
if( heif_image_handle_get_width( heif->handle )
|
||||
!= heif->page_width ||
|
||||
heif_image_handle_get_height( heif->handle )
|
||||
!= heif->page_height ) {
|
||||
!= heif->page_height ||
|
||||
heif_image_handle_get_luma_bits_per_pixel(
|
||||
heif->handle )
|
||||
!= heif->bits_per_pixel ) {
|
||||
vips_error( class->nickname, "%s",
|
||||
_( "not all pages are the same size" ) );
|
||||
return( -1 );
|
||||
@ -761,6 +801,7 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
|
||||
#ifdef DEBUG
|
||||
printf( "page_width = %d\n", heif->page_width );
|
||||
printf( "page_height = %d\n", heif->page_height );
|
||||
printf( "bits_per_pixel = %d\n", heif->bits_per_pixel );
|
||||
|
||||
printf( "n_top = %d\n", heif->n_top );
|
||||
for( i = 0; i < heif->n_top; i++ ) {
|
||||
@ -771,6 +812,8 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
|
||||
heif_image_handle_get_width( heif->handle ) );
|
||||
printf( " height = %d\n",
|
||||
heif_image_handle_get_height( heif->handle ) );
|
||||
printf( " bits_per_pixel = %d\n",
|
||||
heif_image_handle_get_luma_bits_per_pixel( heif->handle ) );
|
||||
printf( " has_depth = %d\n",
|
||||
heif_image_handle_has_depth_image( heif->handle ) );
|
||||
printf( " has_alpha = %d\n",
|
||||
@ -838,6 +881,25 @@ vips__heif_image_print( struct heif_image *img )
|
||||
}
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Pick a chroma format. Shared with heifsave.
|
||||
*/
|
||||
int
|
||||
vips__heif_chroma( int bits_per_pixel, gboolean has_alpha )
|
||||
{
|
||||
if( bits_per_pixel == 8 ) {
|
||||
if( has_alpha )
|
||||
return( heif_chroma_interleaved_RGBA );
|
||||
else
|
||||
return( heif_chroma_interleaved_RGB );
|
||||
}
|
||||
else {
|
||||
if( has_alpha )
|
||||
return( heif_chroma_interleaved_RRGGBBAA_BE );
|
||||
else
|
||||
return( heif_chroma_interleaved_RRGGBB_BE );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_heif_generate( VipsRegion *or,
|
||||
void *seq, void *a, void *b, gboolean *stop )
|
||||
@ -861,18 +923,14 @@ vips_foreign_load_heif_generate( VipsRegion *or,
|
||||
if( !heif->img ) {
|
||||
struct heif_error error;
|
||||
struct heif_decoding_options *options;
|
||||
enum heif_chroma chroma = heif->has_alpha ?
|
||||
heif_chroma_interleaved_RGBA :
|
||||
heif_chroma_interleaved_RGB;
|
||||
enum heif_chroma chroma =
|
||||
vips__heif_chroma( heif->bits_per_pixel,
|
||||
heif->has_alpha );
|
||||
|
||||
options = heif_decoding_options_alloc();
|
||||
#ifdef HAVE_HEIF_DECODING_OPTIONS_CONVERT_HDR_TO_8BIT
|
||||
/* VIPS_FORMAT_UCHAR is assumed so downsample HDR to 8bpc
|
||||
*/
|
||||
options->convert_hdr_to_8bit = TRUE;
|
||||
#endif /*HAVE_HEIF_DECODING_OPTIONS_CONVERT_HDR_TO_8BIT*/
|
||||
error = heif_decode_image( heif->handle, &heif->img,
|
||||
heif_colorspace_RGB, chroma,
|
||||
heif_colorspace_RGB,
|
||||
chroma,
|
||||
options );
|
||||
heif_decoding_options_free( options );
|
||||
if( error.code ) {
|
||||
@ -914,6 +972,26 @@ vips_foreign_load_heif_generate( VipsRegion *or,
|
||||
heif->data + heif->stride * line,
|
||||
VIPS_IMAGE_SIZEOF_LINE( or->im ) );
|
||||
|
||||
/* We may need to swap bytes and shift to fill 16 bits.
|
||||
*/
|
||||
if( heif->bits_per_pixel > 8 ) {
|
||||
int shift = 16 - heif->bits_per_pixel;
|
||||
int ne = VIPS_REGION_N_ELEMENTS( or );
|
||||
|
||||
int i;
|
||||
VipsPel *p;
|
||||
|
||||
p = VIPS_REGION_ADDR( or, 0, r->top );
|
||||
for( i = 0; i < ne; i++ ) {
|
||||
/* We've asked for big endian, we must write native.
|
||||
*/
|
||||
guint16 v = ((p[0] << 8) | p[1]) << shift;
|
||||
|
||||
*((guint16 *) p) = v;
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
* - move GObject part to vips2heif.c
|
||||
* 30/7/21
|
||||
* - rename "speed" as "effort" for consistency with other savers
|
||||
* 22/12/21
|
||||
* - add >8 bit support
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -46,6 +48,21 @@
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
|
||||
TODO:
|
||||
|
||||
what about a 16-bit PNG saved with bitdepth=8? does this work?
|
||||
|
||||
no!
|
||||
|
||||
what about a 8-bit PNG saved with bitdepth=12? does this work?
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
@ -75,6 +92,10 @@ typedef struct _VipsForeignSaveHeif {
|
||||
*/
|
||||
int Q;
|
||||
|
||||
/* bitdepth to save at for >8 bit images.
|
||||
*/
|
||||
int bitdepth;
|
||||
|
||||
/* Lossless compression.
|
||||
*/
|
||||
gboolean lossless;
|
||||
@ -277,6 +298,73 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_heif_pack( VipsForeignSaveHeif *heif,
|
||||
VipsPel *q, VipsPel *p, int ne )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( heif->image->BandFmt == VIPS_FORMAT_UCHAR &&
|
||||
heif->bitdepth == 8 )
|
||||
/* Most common cases -- 8 bit to 8 bit.
|
||||
*/
|
||||
memcpy( q, p, ne );
|
||||
else if( heif->image->BandFmt == VIPS_FORMAT_UCHAR &&
|
||||
heif->bitdepth > 8 ) {
|
||||
/* 8-bit source, write a bigendian short, shifted up.
|
||||
*/
|
||||
int shift = heif->bitdepth - 8;
|
||||
|
||||
for( i = 0; i < ne; i++ ) {
|
||||
guint16 v = p[i] << shift;
|
||||
|
||||
q[0] = v >> 8;
|
||||
q[1] = v;
|
||||
|
||||
q += 2;
|
||||
}
|
||||
}
|
||||
else if( heif->image->BandFmt == VIPS_FORMAT_USHORT &&
|
||||
heif->bitdepth <= 8 ) {
|
||||
/* 16-bit native byte order source, 8 bit write.
|
||||
*/
|
||||
int shift = 16 - heif->bitdepth;
|
||||
|
||||
for( i = 0; i < ne; i++ ) {
|
||||
guint16 v = *((gushort *) p) >> shift;
|
||||
|
||||
q[i] = v;
|
||||
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
else if( heif->image->BandFmt == VIPS_FORMAT_USHORT &&
|
||||
heif->bitdepth > 8 ) {
|
||||
/* 16-bit native byte order source, 16 bit bigendian write.
|
||||
*/
|
||||
int shift = 16 - heif->bitdepth;
|
||||
|
||||
for( i = 0; i < ne; i++ ) {
|
||||
guint16 v = *((gushort *) p) >> shift;
|
||||
|
||||
q[0] = v >> 8;
|
||||
q[1] = v;
|
||||
|
||||
p += 2;
|
||||
q += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VipsObjectClass *class = VIPS_OBJECT_CLASS( heif );
|
||||
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unimplemeted format conversion" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
|
||||
void *a )
|
||||
@ -297,11 +385,12 @@ vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
|
||||
*/
|
||||
int page = (area->top + y) / heif->page_height;
|
||||
int line = (area->top + y) % heif->page_height;
|
||||
|
||||
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y );
|
||||
VipsPel *q = heif->data + line * heif->stride;
|
||||
|
||||
memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( region->im ) );
|
||||
if( vips_foreign_save_heif_pack( heif,
|
||||
q, p, VIPS_REGION_N_ELEMENTS( region ) ) )
|
||||
return( -1 );
|
||||
|
||||
/* Did we just write the final line? Write as a new page
|
||||
* into the output.
|
||||
@ -350,6 +439,13 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
!vips_object_argument_isset( object, "effort" ) )
|
||||
heif->effort = 9 - heif->speed;
|
||||
|
||||
/* Default 12 bit save for ushort. HEIC (for example) implements
|
||||
* 8 / 10 / 12.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "bitdepth" ) )
|
||||
heif->bitdepth = save->ready->BandFmt == VIPS_FORMAT_UCHAR ?
|
||||
8 : 12;
|
||||
|
||||
/* Make a copy of the image in case we modify the metadata eg. for
|
||||
* exif_update.
|
||||
*/
|
||||
@ -419,9 +515,8 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
#endif /*DEBUG*/
|
||||
error = heif_image_create( heif->page_width, heif->page_height,
|
||||
heif_colorspace_RGB,
|
||||
vips_image_hasalpha( heif->image ) ?
|
||||
heif_chroma_interleaved_RGBA :
|
||||
heif_chroma_interleaved_RGB,
|
||||
vips__heif_chroma( heif->bitdepth,
|
||||
vips_image_hasalpha( heif->image ) ),
|
||||
&heif->img );
|
||||
if( error.code ) {
|
||||
vips__heif_error( &error );
|
||||
@ -430,7 +525,7 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
|
||||
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
|
||||
heif->page_width, heif->page_height,
|
||||
vips_image_hasalpha( heif->image ) ? 32 : 24 );
|
||||
heif->bitdepth );
|
||||
if( error.code ) {
|
||||
vips__heif_error( &error );
|
||||
return( -1 );
|
||||
@ -465,13 +560,12 @@ vips_foreign_save_heif_build( VipsObject *object )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
#define US VIPS_FORMAT_USHORT
|
||||
|
||||
static int vips_heif_bandfmt[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, UC, UC, UC, UC, UC, UC, UC, UC
|
||||
UC, UC, US, US, US, US, US, US, US, US
|
||||
};
|
||||
|
||||
static void
|
||||
@ -499,6 +593,13 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
|
||||
1, 100, 50 );
|
||||
|
||||
VIPS_ARG_INT( class, "bitdepth", 11,
|
||||
_( "Bit depth" ),
|
||||
_( "Number of bits per pixel" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, bitdepth ),
|
||||
1, 16, 12 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "lossless", 13,
|
||||
_( "Lossless" ),
|
||||
_( "Enable lossless compression" ),
|
||||
@ -543,6 +644,7 @@ vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
|
||||
{
|
||||
heif->ctx = heif_context_alloc();
|
||||
heif->Q = 50;
|
||||
heif->bitdepth = 12;
|
||||
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
|
||||
heif->effort = 4;
|
||||
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
||||
|
@ -229,6 +229,7 @@ void *vips__foreign_nifti_map( VipsNiftiMapFn fn, void *a, void *b );
|
||||
extern const char *vips__heic_suffs[];
|
||||
extern const char *vips__avif_suffs[];
|
||||
extern const char *vips__heif_suffs[];
|
||||
int vips__heif_chroma( int bits_per_pixel, gboolean has_alpha );
|
||||
|
||||
extern const char *vips__jp2k_suffs[];
|
||||
int vips__foreign_load_jp2k_decompress( VipsImage *out,
|
||||
|
@ -1238,6 +1238,47 @@ class TestForeign:
|
||||
y = pyvips.Image.new_from_buffer(buf, "")
|
||||
assert y.get("exif-ifd0-XPComment").startswith("banana")
|
||||
|
||||
@skip_if_no("heifsave")
|
||||
@pytest.mark.skipif(sys.platform == "darwin", reason="fails with latest libheif/aom from Homebrew")
|
||||
def test_heicsave_16_to_12(self):
|
||||
rgb16 = self.colour.colourspace("rgb16")
|
||||
data = rgb16.heifsave_buffer(lossless=True)
|
||||
im = pyvips.Image.heifload_buffer(data)
|
||||
|
||||
assert(im.width == rgb16.width)
|
||||
assert(im.format == rgb16.format)
|
||||
assert(im.interpretation == rgb16.interpretation)
|
||||
assert(im.get("heif-bitdepth") == 12)
|
||||
# good grief, some kind of lossless
|
||||
assert((im - rgb16).abs().max() < 3000)
|
||||
|
||||
@skip_if_no("heifsave")
|
||||
@pytest.mark.skipif(sys.platform == "darwin", reason="fails with latest libheif/aom from Homebrew")
|
||||
def test_heicsave_16_to_8(self):
|
||||
rgb16 = self.colour.colourspace("rgb16")
|
||||
data = rgb16.heifsave_buffer(lossless=True, bitdepth=8)
|
||||
im = pyvips.Image.heifload_buffer(data)
|
||||
|
||||
assert(im.width == rgb16.width)
|
||||
assert(im.format == "uchar")
|
||||
assert(im.interpretation == "srgb")
|
||||
assert(im.get("heif-bitdepth") == 8)
|
||||
# good grief, some kind of lossless
|
||||
assert((im - rgb16 / 256).abs().max() < 80)
|
||||
|
||||
@skip_if_no("heifsave")
|
||||
@pytest.mark.skipif(sys.platform == "darwin", reason="fails with latest libheif/aom from Homebrew")
|
||||
def test_heicsave_8_to_16(self):
|
||||
data = self.colour.heifsave_buffer(lossless=True, bitdepth=12)
|
||||
im = pyvips.Image.heifload_buffer(data)
|
||||
|
||||
assert(im.width == self.colour.width)
|
||||
assert(im.format == "ushort")
|
||||
assert(im.interpretation == "rgb16")
|
||||
assert(im.get("heif-bitdepth") == 12)
|
||||
# good grief, some kind of lossless
|
||||
assert((im - self.colour * 256).abs().max() < 3000)
|
||||
|
||||
@skip_if_no("jp2kload")
|
||||
def test_jp2kload(self):
|
||||
def jp2k_valid(im):
|
||||
|
Loading…
Reference in New Issue
Block a user