reorganise loadable modules
- heifload operation now defined in heifload.c, etc. - C API wrappers moved to foreign.c
This commit is contained in:
parent
f637206e8d
commit
def025da3e
@ -132,22 +132,22 @@ vips_magick_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(MAGICK_CFLAGS)
|
|||||||
vips_magick_la_LDFLAGS = $(MODULE_LDFLAGS)
|
vips_magick_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
vips_magick_la_LIBADD = $(MODULE_LIBADD) $(MAGICK_LIBS)
|
vips_magick_la_LIBADD = $(MODULE_LIBADD) $(MAGICK_LIBS)
|
||||||
|
|
||||||
vips_jxl_la_SOURCES = module/jxl.c foreign/jxl2vips.c foreign/vips2jxl.c
|
vips_jxl_la_SOURCES = module/jxl.c foreign/jxlload.c foreign/jxlsave.c
|
||||||
vips_jxl_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(LIBJXL_CFLAGS)
|
vips_jxl_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(LIBJXL_CFLAGS)
|
||||||
vips_jxl_la_LDFLAGS = $(MODULE_LDFLAGS)
|
vips_jxl_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
vips_jxl_la_LIBADD = $(MODULE_LIBADD) $(LIBJXL_LIBS)
|
vips_jxl_la_LIBADD = $(MODULE_LIBADD) $(LIBJXL_LIBS)
|
||||||
|
|
||||||
vips_heif_la_SOURCES = module/heif.c foreign/heif2vips.c foreign/vips2heif.c
|
vips_heif_la_SOURCES = module/heif.c foreign/heifload.c foreign/heifsave.c
|
||||||
vips_heif_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(HEIF_CFLAGS)
|
vips_heif_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(HEIF_CFLAGS)
|
||||||
vips_heif_la_LDFLAGS = $(MODULE_LDFLAGS)
|
vips_heif_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
vips_heif_la_LIBADD = $(MODULE_LIBADD) $(HEIF_LIBS)
|
vips_heif_la_LIBADD = $(MODULE_LIBADD) $(HEIF_LIBS)
|
||||||
|
|
||||||
vips_poppler_la_SOURCES = module/poppler.c foreign/poppler2vips.c
|
vips_poppler_la_SOURCES = module/poppler.c foreign/popplerload.c
|
||||||
vips_poppler_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(POPPLER_CFLAGS)
|
vips_poppler_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(POPPLER_CFLAGS)
|
||||||
vips_poppler_la_LDFLAGS = $(MODULE_LDFLAGS)
|
vips_poppler_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
vips_poppler_la_LIBADD = $(MODULE_LIBADD) $(POPPLER_LIBS)
|
vips_poppler_la_LIBADD = $(MODULE_LIBADD) $(POPPLER_LIBS)
|
||||||
|
|
||||||
vips_openslide_la_SOURCES = module/openslide.c foreign/openslide2vips.c
|
vips_openslide_la_SOURCES = module/openslide.c foreign/openslideload.c
|
||||||
vips_openslide_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(OPENSLIDE_CFLAGS)
|
vips_openslide_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(OPENSLIDE_CFLAGS)
|
||||||
vips_openslide_la_LDFLAGS = $(MODULE_LDFLAGS)
|
vips_openslide_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||||
vips_openslide_la_LIBADD = $(MODULE_LIBADD) $(OPENSLIDE_LIBS)
|
vips_openslide_la_LIBADD = $(MODULE_LIBADD) $(OPENSLIDE_LIBS)
|
||||||
|
@ -15,10 +15,6 @@ libforeign_la_SOURCES = \
|
|||||||
fitsload.c \
|
fitsload.c \
|
||||||
fitssave.c \
|
fitssave.c \
|
||||||
foreign.c \
|
foreign.c \
|
||||||
heifload.c \
|
|
||||||
heifsave.c \
|
|
||||||
jxlload.c \
|
|
||||||
jxlsave.c \
|
|
||||||
jp2kload.c \
|
jp2kload.c \
|
||||||
jp2ksave.c \
|
jp2ksave.c \
|
||||||
jpeg2vips.c \
|
jpeg2vips.c \
|
||||||
@ -36,9 +32,7 @@ libforeign_la_SOURCES = \
|
|||||||
nsgifload.c \
|
nsgifload.c \
|
||||||
openexr2vips.c \
|
openexr2vips.c \
|
||||||
openexrload.c \
|
openexrload.c \
|
||||||
openslideload.c \
|
|
||||||
pdfiumload.c \
|
pdfiumload.c \
|
||||||
pdfload.c \
|
|
||||||
pforeign.h \
|
pforeign.h \
|
||||||
pngload.c \
|
pngload.c \
|
||||||
pngsave.c \
|
pngsave.c \
|
||||||
@ -81,19 +75,19 @@ libforeign_la_SOURCES += \
|
|||||||
endif # !MAGICK_MODULE
|
endif # !MAGICK_MODULE
|
||||||
|
|
||||||
if !LIBJXL_MODULE
|
if !LIBJXL_MODULE
|
||||||
libforeign_la_SOURCES += jxl2vips.c vips2jxl.c
|
libforeign_la_SOURCES += jxlload.c jxlsave.c
|
||||||
endif # !LIBJXL_MODULE
|
endif # !LIBJXL_MODULE
|
||||||
|
|
||||||
if !HEIF_MODULE
|
if !HEIF_MODULE
|
||||||
libforeign_la_SOURCES += heif2vips.c vips2heif.c
|
libforeign_la_SOURCES += heifload.c heifsave.c
|
||||||
endif # !HEIF_MODULE
|
endif # !HEIF_MODULE
|
||||||
|
|
||||||
if !POPPLER_MODULE
|
if !POPPLER_MODULE
|
||||||
libforeign_la_SOURCES += poppler2vips.c
|
libforeign_la_SOURCES += popplerload.c
|
||||||
endif # !POPPLER_MODULE
|
endif # !POPPLER_MODULE
|
||||||
|
|
||||||
if !OPENSLIDE_MODULE
|
if !OPENSLIDE_MODULE
|
||||||
libforeign_la_SOURCES += openslide2vips.c
|
libforeign_la_SOURCES += openslideload.c
|
||||||
endif # !OPENSLIDE_MODULE
|
endif # !OPENSLIDE_MODULE
|
||||||
|
|
||||||
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@
|
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@
|
||||||
|
@ -2080,6 +2080,688 @@ vips_foreign_find_save_buffer( const char *name )
|
|||||||
return( G_OBJECT_CLASS_NAME( save_class ) );
|
return( G_OBJECT_CLASS_NAME( save_class ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* C API wrappers for loadable modules go here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifload:
|
||||||
|
* @filename: file to load
|
||||||
|
* @out: (out): decompressed image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, page (top-level image number) to read
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @thumbnail: %gboolean, fetch thumbnail instead of image
|
||||||
|
*
|
||||||
|
* Read a HEIF image file into a VIPS image.
|
||||||
|
*
|
||||||
|
* Use @page to select a page to render, numbering from zero. If neither @n
|
||||||
|
* nor @page are set, @page defaults to the primary page, otherwise to 0.
|
||||||
|
*
|
||||||
|
* Use @n to select the number of pages to render. The default is 1. Pages are
|
||||||
|
* rendered in a vertical column. Set to -1 to mean "until the end of the
|
||||||
|
* document". Use vips_grid() to reorganise pages.
|
||||||
|
*
|
||||||
|
* HEIF images have a primary image. The metadata item `heif-primary` gives
|
||||||
|
* the page number of the primary.
|
||||||
|
*
|
||||||
|
* If @thumbnail is %TRUE, then fetch a stored thumbnail rather than the
|
||||||
|
* image.
|
||||||
|
*
|
||||||
|
* See also: vips_image_new_from_file().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifload( const char *filename, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "heifload", ap, filename, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifload_buffer:
|
||||||
|
* @buf: (array length=len) (element-type guint8): memory area to load
|
||||||
|
* @len: (type gsize): size of memory area
|
||||||
|
* @out: (out): image to write
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, page (top-level image number) to read
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @thumbnail: %gboolean, fetch thumbnail instead of image
|
||||||
|
*
|
||||||
|
* Read a HEIF image file into a VIPS image.
|
||||||
|
* Exactly as vips_heifload(), but read from a memory buffer.
|
||||||
|
*
|
||||||
|
* You must not free the buffer while @out is active. The
|
||||||
|
* #VipsObject::postclose signal on @out is a good place to free.
|
||||||
|
*
|
||||||
|
* See also: vips_heifload().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
VipsBlob *blob;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* We don't take a copy of the data or free it.
|
||||||
|
*/
|
||||||
|
blob = vips_blob_new( NULL, buf, len );
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "heifload_buffer", ap, blob, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifload_source:
|
||||||
|
* @source: source to load from
|
||||||
|
* @out: (out): image to write
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, page (top-level image number) to read
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @thumbnail: %gboolean, fetch thumbnail instead of image
|
||||||
|
*
|
||||||
|
* Exactly as vips_heifload(), but read from a source.
|
||||||
|
*
|
||||||
|
* See also: vips_heifload().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifload_source( VipsSource *source, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "heifload_source", ap, source, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifsave: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @filename: file to write to
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @Q: %gint, quality factor
|
||||||
|
* * @lossless: %gboolean, enable lossless encoding
|
||||||
|
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||||
|
* * @speed: %gint, encoding speed
|
||||||
|
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||||
|
*
|
||||||
|
* Write a VIPS image to a file in HEIF format.
|
||||||
|
*
|
||||||
|
* Use @Q to set the compression factor. Default 50, which seems to be roughly
|
||||||
|
* what the iphone uses. Q 30 gives about the same quality as JPEG Q 75.
|
||||||
|
*
|
||||||
|
* Set @lossless %TRUE to switch to lossless compression.
|
||||||
|
*
|
||||||
|
* Use @compression to set the encoder e.g. HEVC, AVC, AV1. It defaults to AV1
|
||||||
|
* if the target filename ends with ".avif", otherwise HEVC.
|
||||||
|
*
|
||||||
|
* Use @speed to control the CPU effort spent improving compression.
|
||||||
|
* This is currently only applicable to AV1 encoders. Defaults to 5, 0 is
|
||||||
|
* slowest, 9 is fastest.
|
||||||
|
*
|
||||||
|
* Chroma subsampling is normally automatically disabled for Q >= 90. You can
|
||||||
|
* force the subsampling mode with @subsample_mode.
|
||||||
|
*
|
||||||
|
* See also: vips_image_write_to_file(), vips_heifload().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifsave( VipsImage *in, const char *filename, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, filename );
|
||||||
|
result = vips_call_split( "heifsave", ap, in, filename );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifsave_buffer: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @buf: (array length=len) (element-type guint8): return output buffer here
|
||||||
|
* @len: (type gsize): return output length here
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @Q: %gint, quality factor
|
||||||
|
* * @lossless: %gboolean, enable lossless encoding
|
||||||
|
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||||
|
* * @speed: %gint, encoding speed
|
||||||
|
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||||
|
*
|
||||||
|
* As vips_heifsave(), but save to a memory buffer.
|
||||||
|
*
|
||||||
|
* The address of the buffer is returned in @obuf, the length of the buffer in
|
||||||
|
* @olen. You are responsible for freeing the buffer with g_free() when you
|
||||||
|
* are done with it.
|
||||||
|
*
|
||||||
|
* See also: vips_heifsave(), vips_image_write_to_file().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
VipsArea *area;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
area = NULL;
|
||||||
|
|
||||||
|
va_start( ap, len );
|
||||||
|
result = vips_call_split( "heifsave_buffer", ap, in, &area );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
if( !result &&
|
||||||
|
area ) {
|
||||||
|
if( buf ) {
|
||||||
|
*buf = area->data;
|
||||||
|
area->free_fn = NULL;
|
||||||
|
}
|
||||||
|
if( len )
|
||||||
|
*len = area->length;
|
||||||
|
|
||||||
|
vips_area_unref( area );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_heifsave_target: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @target: save image to this target
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @Q: %gint, quality factor
|
||||||
|
* * @lossless: %gboolean, enable lossless encoding
|
||||||
|
* * @compression: #VipsForeignHeifCompression, write with this compression
|
||||||
|
* * @speed: %gint, encoding speed
|
||||||
|
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
||||||
|
*
|
||||||
|
* As vips_heifsave(), but save to a target.
|
||||||
|
*
|
||||||
|
* See also: vips_heifsave(), vips_image_write_to_target().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_heifsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, target );
|
||||||
|
result = vips_call_split( "heifsave_target", ap, in, target );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlload:
|
||||||
|
* @filename: file to load
|
||||||
|
* @out: (out): decompressed image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Read a JPEG-XL image.
|
||||||
|
*
|
||||||
|
* The JPEG-XL loader and saver are experimental features and may change
|
||||||
|
* in future libvips versions.
|
||||||
|
*
|
||||||
|
* See also: vips_image_new_from_file().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlload( const char *filename, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "jxlload", ap, filename, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlload_buffer:
|
||||||
|
* @buf: (array length=len) (element-type guint8): memory area to load
|
||||||
|
* @len: (type gsize): size of memory area
|
||||||
|
* @out: (out): image to write
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Exactly as vips_jxlload(), but read from a buffer.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
VipsBlob *blob;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* We don't take a copy of the data or free it.
|
||||||
|
*/
|
||||||
|
blob = vips_blob_new( NULL, buf, len );
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "jxlload_buffer", ap, blob, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlload_source:
|
||||||
|
* @source: source to load from
|
||||||
|
* @out: (out): decompressed image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Exactly as vips_jxlload(), but read from a source.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlload_source( VipsSource *source, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "jxlload_source", ap, source, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlsave: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @filename: file to write to
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @tier: %gint, decode speed tier
|
||||||
|
* * @distance: %gdouble, maximum encoding error
|
||||||
|
* * @effort: %gint, encoding effort
|
||||||
|
* * @lossless: %gboolean, enables lossless compression
|
||||||
|
* * @Q: %gint, quality setting
|
||||||
|
*
|
||||||
|
* Write a VIPS image to a file in JPEG-XL format.
|
||||||
|
*
|
||||||
|
* The JPEG-XL loader and saver are experimental features and may change
|
||||||
|
* in future libvips versions.
|
||||||
|
*
|
||||||
|
* @tier sets the overall decode speed the encoder will target. Minimum is 0
|
||||||
|
* (highest quality), and maximum is 4 (lowest quality). Default is 0.
|
||||||
|
*
|
||||||
|
* @distance sets the target maximum encoding error. Minimum is 0
|
||||||
|
* (highest quality), and maximum is 15 (lowest quality). Default is 1.0
|
||||||
|
* (visually lossless).
|
||||||
|
*
|
||||||
|
* As a convenience, you can also use @Q to set @distance. @Q uses
|
||||||
|
* approximately the same scale as regular JPEG.
|
||||||
|
*
|
||||||
|
* Set @lossless to enable lossless compresion.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlsave( VipsImage *in, const char *filename, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, filename );
|
||||||
|
result = vips_call_split( "jxlsave", ap, in, filename );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlsave_buffer: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @buf: (array length=len) (element-type guint8): return output buffer here
|
||||||
|
* @len: (type gsize): return output length here
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @tier: %gint, decode speed tier
|
||||||
|
* * @distance: %gdouble, maximum encoding error
|
||||||
|
* * @effort: %gint, encoding effort
|
||||||
|
* * @lossless: %gboolean, enables lossless compression
|
||||||
|
* * @Q: %gint, quality setting
|
||||||
|
*
|
||||||
|
* As vips_jxlsave(), but save to a memory buffer.
|
||||||
|
*
|
||||||
|
* See also: vips_jxlsave(), vips_image_write_to_target().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
VipsArea *area;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
area = NULL;
|
||||||
|
|
||||||
|
va_start( ap, len );
|
||||||
|
result = vips_call_split( "jxlsave_buffer", ap, in, &area );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
if( !result &&
|
||||||
|
area ) {
|
||||||
|
if( buf ) {
|
||||||
|
*buf = area->data;
|
||||||
|
area->free_fn = NULL;
|
||||||
|
}
|
||||||
|
if( len )
|
||||||
|
*len = area->length;
|
||||||
|
|
||||||
|
vips_area_unref( area );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_jxlsave_target: (method)
|
||||||
|
* @in: image to save
|
||||||
|
* @target: save image to this target
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @tier: %gint, decode speed tier
|
||||||
|
* * @distance: %gdouble, maximum encoding error
|
||||||
|
* * @effort: %gint, encoding effort
|
||||||
|
* * @lossless: %gboolean, enables lossless compression
|
||||||
|
* * @Q: %gint, quality setting
|
||||||
|
*
|
||||||
|
* As vips_jxlsave(), but save to a target.
|
||||||
|
*
|
||||||
|
* See also: vips_jxlsave(), vips_image_write_to_target().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_jxlsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, target );
|
||||||
|
result = vips_call_split( "jxlsave_target", ap, in, target );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_pdfload:
|
||||||
|
* @filename: file to load
|
||||||
|
* @out: (out): output image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, load this page, numbered from zero
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @dpi: %gdouble, render at this DPI
|
||||||
|
* * @scale: %gdouble, scale render by this factor
|
||||||
|
* * @background: #VipsArrayDouble background colour
|
||||||
|
*
|
||||||
|
* Render a PDF file into a VIPS image.
|
||||||
|
*
|
||||||
|
* The output image is always RGBA --- CMYK PDFs will be
|
||||||
|
* converted. If you need CMYK bitmaps, you should use vips_magickload()
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* Use @page to select a page to render, numbering from zero.
|
||||||
|
*
|
||||||
|
* Use @n to select the number of pages to render. The default is 1. Pages are
|
||||||
|
* rendered in a vertical column, with each individual page aligned to the
|
||||||
|
* left. Set to -1 to mean "until the end of the document". Use vips_grid()
|
||||||
|
* to change page layout.
|
||||||
|
*
|
||||||
|
* Use @dpi to set the rendering resolution. The default is 72. Additionally,
|
||||||
|
* you can scale by setting @scale. If you set both, they combine.
|
||||||
|
*
|
||||||
|
* Use @background to set the background RGBA colour. The default is 255
|
||||||
|
* (solid white), use eg. 0 for a transparent background.
|
||||||
|
*
|
||||||
|
* The operation fills a number of header fields with metadata, for example
|
||||||
|
* "pdf-author". They may be useful.
|
||||||
|
*
|
||||||
|
* This function only reads the image header and does not render any pixel
|
||||||
|
* data. Rendering occurs when pixels are accessed.
|
||||||
|
*
|
||||||
|
* See also: vips_image_new_from_file(), vips_magickload().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_pdfload( const char *filename, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "pdfload", ap, filename, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_pdfload_buffer:
|
||||||
|
* @buf: (array length=len) (element-type guint8): memory area to load
|
||||||
|
* @len: (type gsize): size of memory area
|
||||||
|
* @out: (out): image to write
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, load this page, numbered from zero
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @dpi: %gdouble, render at this DPI
|
||||||
|
* * @scale: %gdouble, scale render by this factor
|
||||||
|
* * @background: #VipsArrayDouble background colour
|
||||||
|
*
|
||||||
|
* Read a PDF-formatted memory buffer into a VIPS image. Exactly as
|
||||||
|
* vips_pdfload(), but read from memory.
|
||||||
|
*
|
||||||
|
* You must not free the buffer while @out is active. The
|
||||||
|
* #VipsObject::postclose signal on @out is a good place to free.
|
||||||
|
*
|
||||||
|
* See also: vips_pdfload().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_pdfload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
VipsBlob *blob;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* We don't take a copy of the data or free it.
|
||||||
|
*/
|
||||||
|
blob = vips_blob_new( NULL, buf, len );
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "pdfload_buffer", ap, blob, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_pdfload_source:
|
||||||
|
* @source: source to load from
|
||||||
|
* @out: (out): image to write
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @page: %gint, load this page, numbered from zero
|
||||||
|
* * @n: %gint, load this many pages
|
||||||
|
* * @dpi: %gdouble, render at this DPI
|
||||||
|
* * @scale: %gdouble, scale render by this factor
|
||||||
|
* * @background: #VipsArrayDouble background colour
|
||||||
|
*
|
||||||
|
* Exactly as vips_pdfload(), but read from a source.
|
||||||
|
*
|
||||||
|
* See also: vips_pdfload()
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_pdfload_source( VipsSource *source, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "pdfload_source", ap, source, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_openslideload:
|
||||||
|
* @filename: file to load
|
||||||
|
* @out: (out): decompressed image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @level: %gint, load this level
|
||||||
|
* * @associated: %gchararray, load this associated image
|
||||||
|
* * @attach_associated: %gboolean, attach all associated images as metadata
|
||||||
|
* * @autocrop: %gboolean, crop to image bounds
|
||||||
|
*
|
||||||
|
* Read a virtual slide supported by the OpenSlide library into a VIPS image.
|
||||||
|
* OpenSlide supports images in Aperio, Hamamatsu, MIRAX, Sakura, Trestle,
|
||||||
|
* and Ventana formats.
|
||||||
|
*
|
||||||
|
* To facilitate zooming, virtual slide formats include multiple scaled-down
|
||||||
|
* versions of the high-resolution image. These are typically called
|
||||||
|
* "levels". By default, vips_openslideload() reads the highest-resolution
|
||||||
|
* level (level 0). Set @level to the level number you want.
|
||||||
|
*
|
||||||
|
* In addition to the slide image itself, virtual slide formats sometimes
|
||||||
|
* include additional images, such as a scan of the slide's barcode.
|
||||||
|
* OpenSlide calls these "associated images". To read an associated image,
|
||||||
|
* set @associated to the image's name.
|
||||||
|
* A slide's associated images are listed in the
|
||||||
|
* "slide-associated-images" metadata item.
|
||||||
|
*
|
||||||
|
* If you set @attach_associated, then all associated images are attached as
|
||||||
|
* metadata items. Use vips_image_get_image() on @out to retrieve them. Images
|
||||||
|
* are attached as "openslide-associated-XXXXX", where XXXXX is the name of the
|
||||||
|
* associated image.
|
||||||
|
*
|
||||||
|
* The output of this operator is always RGBA.
|
||||||
|
*
|
||||||
|
* See also: vips_image_new_from_file().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_openslideload( const char *filename, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "openslideload", ap, filename, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_openslideload_source:
|
||||||
|
* @source: source to load from
|
||||||
|
* @out: (out): decompressed image
|
||||||
|
* @...: %NULL-terminated list of optional named arguments
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* * @level: %gint, load this level
|
||||||
|
* * @associated: %gchararray, load this associated image
|
||||||
|
* * @attach_associated: %gboolean, attach all associated images as metadata
|
||||||
|
* * @autocrop: %gboolean, crop to image bounds
|
||||||
|
*
|
||||||
|
* Exactly as vips_openslideload(), but read from a source.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_openslideload_source( VipsSource *source, VipsImage **out, ... )
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
va_start( ap, out );
|
||||||
|
result = vips_call_split( "openslideload_source", ap, source, out );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
return( result );
|
||||||
|
}
|
||||||
|
|
||||||
/* Called from iofuncs to init all operations in this dir. Use a plugin system
|
/* Called from iofuncs to init all operations in this dir. Use a plugin system
|
||||||
* instead?
|
* instead?
|
||||||
*/
|
*/
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,8 @@
|
|||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
#include <vips/intl.h>
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_HEIF_ENCODER
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -56,136 +58,665 @@
|
|||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
#include <vips/internal.h>
|
#include <vips/internal.h>
|
||||||
|
|
||||||
/**
|
#include "pforeign.h"
|
||||||
* vips_heifsave: (method)
|
|
||||||
* @in: image to save
|
#include <libheif/heif.h>
|
||||||
* @filename: file to write to
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
typedef struct _VipsForeignSaveHeif {
|
||||||
*
|
VipsForeignSave parent_object;
|
||||||
* Optional arguments:
|
|
||||||
*
|
/* Where to write (set by subclasses).
|
||||||
* * @Q: %gint, quality factor
|
|
||||||
* * @lossless: %gboolean, enable lossless encoding
|
|
||||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
|
||||||
* * @speed: %gint, encoding speed
|
|
||||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
|
||||||
*
|
|
||||||
* Write a VIPS image to a file in HEIF format.
|
|
||||||
*
|
|
||||||
* Use @Q to set the compression factor. Default 50, which seems to be roughly
|
|
||||||
* what the iphone uses. Q 30 gives about the same quality as JPEG Q 75.
|
|
||||||
*
|
|
||||||
* Set @lossless %TRUE to switch to lossless compression.
|
|
||||||
*
|
|
||||||
* Use @compression to set the encoder e.g. HEVC, AVC, AV1. It defaults to AV1
|
|
||||||
* if the target filename ends with ".avif", otherwise HEVC.
|
|
||||||
*
|
|
||||||
* Use @speed to control the CPU effort spent improving compression.
|
|
||||||
* This is currently only applicable to AV1 encoders. Defaults to 5, 0 is
|
|
||||||
* slowest, 9 is fastest.
|
|
||||||
*
|
|
||||||
* Chroma subsampling is normally automatically disabled for Q >= 90. You can
|
|
||||||
* force the subsampling mode with @subsample_mode.
|
|
||||||
*
|
|
||||||
* See also: vips_image_write_to_file(), vips_heifload().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
*/
|
||||||
int
|
VipsTarget *target;
|
||||||
vips_heifsave( VipsImage *in, const char *filename, ... )
|
|
||||||
|
/* Coding quality factor (1-100).
|
||||||
|
*/
|
||||||
|
int Q;
|
||||||
|
|
||||||
|
/* Lossless compression.
|
||||||
|
*/
|
||||||
|
gboolean lossless;
|
||||||
|
|
||||||
|
/* Compression format
|
||||||
|
*/
|
||||||
|
VipsForeignHeifCompression compression;
|
||||||
|
|
||||||
|
/* CPU effort (0-8).
|
||||||
|
*/
|
||||||
|
int speed;
|
||||||
|
|
||||||
|
/* Chroma subsampling.
|
||||||
|
*/
|
||||||
|
VipsForeignSubsample subsample_mode;
|
||||||
|
|
||||||
|
/* The image we save. This is a copy of save->ready since we need to
|
||||||
|
* be able to update the metadata.
|
||||||
|
*/
|
||||||
|
VipsImage *image;
|
||||||
|
|
||||||
|
int page_width;
|
||||||
|
int page_height;
|
||||||
|
int n_pages;
|
||||||
|
|
||||||
|
struct heif_context *ctx;
|
||||||
|
struct heif_encoder *encoder;
|
||||||
|
|
||||||
|
/* The current page we are writing.
|
||||||
|
*/
|
||||||
|
struct heif_image_handle *handle;
|
||||||
|
|
||||||
|
/* The current page in memory which we build as we scan down the
|
||||||
|
* image.
|
||||||
|
*/
|
||||||
|
struct heif_image *img;
|
||||||
|
|
||||||
|
/* The libheif memory area we fill with pixels from the libvips
|
||||||
|
* pipe.
|
||||||
|
*/
|
||||||
|
uint8_t *data;
|
||||||
|
int stride;
|
||||||
|
|
||||||
|
} VipsForeignSaveHeif;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveClass VipsForeignSaveHeifClass;
|
||||||
|
|
||||||
|
/* Defined in heif2vips.c
|
||||||
|
*/
|
||||||
|
void vips__heif_error( struct heif_error *error );
|
||||||
|
void vips__heif_image_print( struct heif_image *img );
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveHeif, vips_foreign_save_heif,
|
||||||
|
VIPS_TYPE_FOREIGN_SAVE );
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_dispose( GObject *gobject )
|
||||||
{
|
{
|
||||||
va_list ap;
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) gobject;
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, filename );
|
VIPS_UNREF( heif->target );
|
||||||
result = vips_call_split( "heifsave", ap, in, filename );
|
VIPS_UNREF( heif->image );
|
||||||
va_end( ap );
|
VIPS_FREEF( heif_image_release, heif->img );
|
||||||
|
VIPS_FREEF( heif_image_handle_release, heif->handle );
|
||||||
|
VIPS_FREEF( heif_encoder_release, heif->encoder );
|
||||||
|
VIPS_FREEF( heif_context_free, heif->ctx );
|
||||||
|
|
||||||
return( result );
|
G_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
|
||||||
|
dispose( gobject );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
|
||||||
* vips_heifsave_buffer: (method)
|
const struct heif_image_handle *,
|
||||||
* @in: image to save
|
const void *, int );
|
||||||
* @buf: (array length=len) (element-type guint8): return output buffer here
|
|
||||||
* @len: (type gsize): return output length here
|
struct _VipsForeignSaveHeifMetadata {
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
const char *name;
|
||||||
*
|
libheif_metadata_fn saver;
|
||||||
* Optional arguments:
|
} libheif_metadata[] = {
|
||||||
*
|
{ VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
|
||||||
* * @Q: %gint, quality factor
|
{ VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
|
||||||
* * @lossless: %gboolean, enable lossless encoding
|
};
|
||||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
|
||||||
* * @speed: %gint, encoding speed
|
static int
|
||||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
|
||||||
*
|
|
||||||
* As vips_heifsave(), but save to a memory buffer.
|
|
||||||
*
|
|
||||||
* The address of the buffer is returned in @obuf, the length of the buffer in
|
|
||||||
* @olen. You are responsible for freeing the buffer with g_free() when you
|
|
||||||
* are done with it.
|
|
||||||
*
|
|
||||||
* See also: vips_heifsave(), vips_image_write_to_file().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
int i;
|
||||||
VipsArea *area;
|
struct heif_error error;
|
||||||
int result;
|
|
||||||
|
|
||||||
area = NULL;
|
/* Rebuild exif from tags, if we'll be saving it.
|
||||||
|
*/
|
||||||
|
if( vips_image_get_typeof( heif->image, VIPS_META_EXIF_NAME ) )
|
||||||
|
if( vips__exif_update( heif->image ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
va_start( ap, len );
|
for( i = 0; i < VIPS_NUMBER( libheif_metadata ); i++ )
|
||||||
result = vips_call_split( "heifsave_buffer", ap, in, &area );
|
if( vips_image_get_typeof( heif->image,
|
||||||
va_end( ap );
|
libheif_metadata[i].name ) ) {
|
||||||
|
const void *data;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
if( !result &&
|
#ifdef DEBUG
|
||||||
area ) {
|
printf( "attaching %s ..\n",
|
||||||
if( buf ) {
|
libheif_metadata[i].name );
|
||||||
*buf = area->data;
|
#endif /*DEBUG*/
|
||||||
area->free_fn = NULL;
|
|
||||||
|
if( vips_image_get_blob( heif->image,
|
||||||
|
libheif_metadata[i].name, &data, &length ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
error = libheif_metadata[i].saver( heif->ctx,
|
||||||
|
heif->handle, data, length );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
}
|
}
|
||||||
if( len )
|
|
||||||
*len = area->length;
|
|
||||||
|
|
||||||
vips_area_unref( area );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return( result );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int
|
||||||
* vips_heifsave_target: (method)
|
vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
|
||||||
* @in: image to save
|
|
||||||
* @target: save image to this target
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Optional arguments:
|
|
||||||
*
|
|
||||||
* * @Q: %gint, quality factor
|
|
||||||
* * @lossless: %gboolean, enable lossless encoding
|
|
||||||
* * @compression: #VipsForeignHeifCompression, write with this compression
|
|
||||||
* * @speed: %gint, encoding speed
|
|
||||||
* * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode
|
|
||||||
*
|
|
||||||
* As vips_heifsave(), but save to a target.
|
|
||||||
*
|
|
||||||
* See also: vips_heifsave(), vips_image_write_to_target().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_heifsave_target( VipsImage *in, VipsTarget *target, ... )
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
VipsForeignSave *save = (VipsForeignSave *) heif;
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, target );
|
struct heif_error error;
|
||||||
result = vips_call_split( "heifsave_target", ap, in, target );
|
struct heif_encoding_options *options;
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
return( result );
|
#ifdef HAVE_HEIF_COLOR_PROFILE
|
||||||
|
if( !save->strip &&
|
||||||
|
vips_image_get_typeof( heif->image, VIPS_META_ICC_NAME ) ) {
|
||||||
|
const void *data;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "attaching profile ..\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
if( vips_image_get_blob( heif->image,
|
||||||
|
VIPS_META_ICC_NAME, &data, &length ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* FIXME .. also see heif_image_set_nclx_color_profile()
|
||||||
|
*/
|
||||||
|
error = heif_image_set_raw_color_profile( heif->img,
|
||||||
|
"rICC", data, length );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /*HAVE_HEIF_COLOR_PROFILE*/
|
||||||
|
|
||||||
|
options = heif_encoding_options_alloc();
|
||||||
|
if( vips_image_hasalpha( heif->image ) )
|
||||||
|
options->save_alpha_channel = 1;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "encoding ..\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
error = heif_context_encode_image( heif->ctx,
|
||||||
|
heif->img, heif->encoder, options, &heif->handle );
|
||||||
|
|
||||||
|
heif_encoding_options_free( options );
|
||||||
|
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
|
||||||
|
int primary;
|
||||||
|
|
||||||
|
if( vips_image_get_int( heif->image,
|
||||||
|
"heif-primary", &primary ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( page == primary ) {
|
||||||
|
error = heif_context_set_primary_image( heif->ctx,
|
||||||
|
heif->handle );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !save->strip &&
|
||||||
|
vips_foreign_save_heif_write_metadata( heif ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
VIPS_FREEF( heif_image_handle_release, heif->handle );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
|
||||||
|
void *a )
|
||||||
|
{
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) a;
|
||||||
|
|
||||||
|
int y;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_save_heif_write_block: y = %d\n", area->top );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
/* Copy a line at a time into our output image, write each time the
|
||||||
|
* image fills.
|
||||||
|
*/
|
||||||
|
for( y = 0; y < area->height; y++ ) {
|
||||||
|
/* Y in page.
|
||||||
|
*/
|
||||||
|
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 ) );
|
||||||
|
|
||||||
|
/* Did we just write the final line? Write as a new page
|
||||||
|
* into the output.
|
||||||
|
*/
|
||||||
|
if( line == heif->page_height - 1 )
|
||||||
|
if( vips_foreign_save_heif_write_page( heif, page ) )
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct heif_error
|
||||||
|
vips_foreign_save_heif_write( struct heif_context *ctx,
|
||||||
|
const void *data, size_t length, void *userdata )
|
||||||
|
{
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
|
||||||
|
|
||||||
|
struct heif_error error;
|
||||||
|
|
||||||
|
error.code = 0;
|
||||||
|
if( vips_target_write( heif->target, data, length ) )
|
||||||
|
error.code = -1;
|
||||||
|
|
||||||
|
return( error );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_heif_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
||||||
|
|
||||||
|
const char *filename;
|
||||||
|
struct heif_error error;
|
||||||
|
struct heif_writer writer;
|
||||||
|
char *chroma;
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Make a copy of the image in case we modify the metadata eg. for
|
||||||
|
* exif_update.
|
||||||
|
*/
|
||||||
|
if( vips_copy( save->ready, &heif->image, NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Compression defaults to VIPS_FOREIGN_HEIF_COMPRESSION_AV1 for .avif
|
||||||
|
* suffix.
|
||||||
|
*/
|
||||||
|
filename = vips_connection_filename( VIPS_CONNECTION( heif->target ) );
|
||||||
|
if( !vips_object_argument_isset( object, "compression" ) &&
|
||||||
|
filename &&
|
||||||
|
vips_iscasepostfix( filename, ".avif" ) )
|
||||||
|
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
||||||
|
|
||||||
|
error = heif_context_get_encoder_for_format( heif->ctx,
|
||||||
|
(enum heif_compression_format) heif->compression,
|
||||||
|
&heif->encoder );
|
||||||
|
if( error.code ) {
|
||||||
|
if( error.code == heif_error_Unsupported_filetype )
|
||||||
|
vips_error( "heifsave",
|
||||||
|
"%s", _( "Unsupported compression" ) );
|
||||||
|
else
|
||||||
|
vips__heif_error( &error );
|
||||||
|
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
error = heif_encoder_set_lossy_quality( heif->encoder, heif->Q );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
error = heif_encoder_set_lossless( heif->encoder, heif->lossless );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
error = heif_encoder_set_parameter_integer( heif->encoder,
|
||||||
|
"speed", heif->speed );
|
||||||
|
if( error.code &&
|
||||||
|
error.subcode != heif_suberror_Unsupported_parameter ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
|
||||||
|
( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
|
||||||
|
heif->Q >= 90 ) ? "444" : "420";
|
||||||
|
error = heif_encoder_set_parameter_string( heif->encoder,
|
||||||
|
"chroma", chroma );
|
||||||
|
if( error.code &&
|
||||||
|
error.subcode != heif_suberror_Unsupported_parameter ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO .. support extra per-encoder params with
|
||||||
|
* heif_encoder_list_parameters().
|
||||||
|
*/
|
||||||
|
|
||||||
|
heif->page_width = heif->image->Xsize;
|
||||||
|
heif->page_height = vips_image_get_page_height( heif->image );
|
||||||
|
heif->n_pages = heif->image->Ysize / heif->page_height;
|
||||||
|
|
||||||
|
/* Make a heif image the size of a page. We send sink_disc() output
|
||||||
|
* here and write a frame each time it fills.
|
||||||
|
*/
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_save_heif_build:\n" );
|
||||||
|
printf( "\twidth = %d\n", heif->page_width );
|
||||||
|
printf( "\theight = %d\n", heif->page_height );
|
||||||
|
printf( "\talpha = %d\n", vips_image_hasalpha( heif->image ) );
|
||||||
|
#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,
|
||||||
|
&heif->img );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
|
||||||
|
heif->page_width, heif->page_height,
|
||||||
|
vips_image_hasalpha( heif->image ) ? 32 : 24 );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips__heif_image_print( heif->img );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
heif->data = heif_image_get_plane( heif->img,
|
||||||
|
heif_channel_interleaved, &heif->stride );
|
||||||
|
|
||||||
|
/* Write data.
|
||||||
|
*/
|
||||||
|
if( vips_sink_disc( heif->image,
|
||||||
|
vips_foreign_save_heif_write_block, heif ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* This has to come right at the end :-( so there's no support for
|
||||||
|
* incremental writes.
|
||||||
|
*/
|
||||||
|
writer.writer_api_version = 1;
|
||||||
|
writer.write = vips_foreign_save_heif_write;
|
||||||
|
error = heif_context_write( heif->ctx, &writer, heif );
|
||||||
|
if( error.code ) {
|
||||||
|
vips__heif_error( &error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
vips_target_finish( heif->target );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save a bit of typing.
|
||||||
|
*/
|
||||||
|
#define UC VIPS_FORMAT_UCHAR
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||||
|
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||||
|
|
||||||
|
gobject_class->dispose = vips_foreign_save_heif_dispose;
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "heifsave_base";
|
||||||
|
object_class->description = _( "save image in HEIF format" );
|
||||||
|
object_class->build = vips_foreign_save_heif_build;
|
||||||
|
|
||||||
|
foreign_class->suffs = vips__heif_suffs;
|
||||||
|
|
||||||
|
save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
|
||||||
|
save_class->format_table = vips_heif_bandfmt;
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "Q", 10,
|
||||||
|
_( "Q" ),
|
||||||
|
_( "Q factor" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
|
||||||
|
1, 100, 50 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOOL( class, "lossless", 13,
|
||||||
|
_( "Lossless" ),
|
||||||
|
_( "Enable lossless compression" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeif, lossless ),
|
||||||
|
FALSE );
|
||||||
|
|
||||||
|
VIPS_ARG_ENUM( class, "compression", 14,
|
||||||
|
_( "compression" ),
|
||||||
|
_( "Compression format" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeif, compression ),
|
||||||
|
VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
||||||
|
VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "speed", 15,
|
||||||
|
_( "speed" ),
|
||||||
|
_( "CPU effort" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
|
||||||
|
0, 9, 5 );
|
||||||
|
|
||||||
|
VIPS_ARG_ENUM( class, "subsample_mode", 16,
|
||||||
|
_( "Subsample mode" ),
|
||||||
|
_( "Select chroma subsample operation mode" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
|
||||||
|
VIPS_TYPE_FOREIGN_SUBSAMPLE,
|
||||||
|
VIPS_FOREIGN_SUBSAMPLE_AUTO );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
|
||||||
|
{
|
||||||
|
heif->ctx = heif_context_alloc();
|
||||||
|
heif->Q = 50;
|
||||||
|
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
|
||||||
|
heif->speed = 5;
|
||||||
|
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveHeifFile {
|
||||||
|
VipsForeignSaveHeif parent_object;
|
||||||
|
|
||||||
|
/* Filename for save.
|
||||||
|
*/
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
} VipsForeignSaveHeifFile;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifFileClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveHeifFile, vips_foreign_save_heif_file,
|
||||||
|
vips_foreign_save_heif_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_heif_file_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
||||||
|
VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
|
||||||
|
|
||||||
|
if( !(heif->target = vips_target_new_to_file( file->filename )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "heifsave";
|
||||||
|
object_class->build = vips_foreign_save_heif_file_build;
|
||||||
|
|
||||||
|
VIPS_ARG_STRING( class, "filename", 1,
|
||||||
|
_( "Filename" ),
|
||||||
|
_( "Filename to save to" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeifFile, filename ),
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_file_init( VipsForeignSaveHeifFile *file )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveHeifBuffer {
|
||||||
|
VipsForeignSaveHeif parent_object;
|
||||||
|
|
||||||
|
/* Save to a buffer.
|
||||||
|
*/
|
||||||
|
VipsArea *buf;
|
||||||
|
|
||||||
|
} VipsForeignSaveHeifBuffer;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer,
|
||||||
|
vips_foreign_save_heif_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_heif_buffer_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
||||||
|
VipsForeignSaveHeifBuffer *buffer =
|
||||||
|
(VipsForeignSaveHeifBuffer *) object;
|
||||||
|
|
||||||
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
if( !(heif->target = vips_target_new_to_memory()) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_buffer_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
g_object_get( heif->target, "blob", &blob, NULL );
|
||||||
|
g_object_set( buffer, "buffer", blob, NULL );
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_buffer_class_init(
|
||||||
|
VipsForeignSaveHeifBufferClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "heifsave_buffer";
|
||||||
|
object_class->build = vips_foreign_save_heif_buffer_build;
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||||
|
_( "Buffer" ),
|
||||||
|
_( "Buffer to save to" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeifBuffer, buf ),
|
||||||
|
VIPS_TYPE_BLOB );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_buffer_init( VipsForeignSaveHeifBuffer *buffer )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveHeifTarget {
|
||||||
|
VipsForeignSaveHeif parent_object;
|
||||||
|
|
||||||
|
VipsTarget *target;
|
||||||
|
} VipsForeignSaveHeifTarget;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifTargetClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveHeifTarget, vips_foreign_save_heif_target,
|
||||||
|
vips_foreign_save_heif_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_heif_target_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
||||||
|
VipsForeignSaveHeifTarget *target =
|
||||||
|
(VipsForeignSaveHeifTarget *) object;
|
||||||
|
|
||||||
|
if( target->target ) {
|
||||||
|
heif->target = target->target;
|
||||||
|
g_object_ref( heif->target );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_target_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_target_class_init(
|
||||||
|
VipsForeignSaveHeifTargetClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "heifsave_target";
|
||||||
|
object_class->build = vips_foreign_save_heif_target_build;
|
||||||
|
|
||||||
|
VIPS_ARG_OBJECT( class, "target", 1,
|
||||||
|
_( "Target" ),
|
||||||
|
_( "Target to save to" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveHeifTarget, target ),
|
||||||
|
VIPS_TYPE_TARGET );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*HAVE_HEIF_ENCODER*/
|
||||||
|
|
||||||
|
/* The C API wrappers are defined in foreign.c.
|
||||||
|
*/
|
||||||
|
@ -1,918 +0,0 @@
|
|||||||
/* load jpeg-xl
|
|
||||||
*
|
|
||||||
* 18/3/20
|
|
||||||
* - from heifload.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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define DEBUG_VERBOSE
|
|
||||||
#define DEBUG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
#include <vips/intl.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <vips/vips.h>
|
|
||||||
#include <vips/debug.h>
|
|
||||||
#include <vips/internal.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBJXL
|
|
||||||
|
|
||||||
#include <jxl/decode.h>
|
|
||||||
#include <jxl/thread_parallel_runner.h>
|
|
||||||
|
|
||||||
#include "pforeign.h"
|
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
*
|
|
||||||
* - add metadata support
|
|
||||||
*
|
|
||||||
* - add animation support
|
|
||||||
*
|
|
||||||
* - add "shrink" option to read out 8x shrunk image?
|
|
||||||
*
|
|
||||||
* - fix scRGB gamma
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define INPUT_BUFFER_SIZE (4096)
|
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadJxl {
|
|
||||||
VipsForeignLoad parent_object;
|
|
||||||
|
|
||||||
/* Source to load from (set by subclasses).
|
|
||||||
*/
|
|
||||||
VipsSource *source;
|
|
||||||
|
|
||||||
/* Page set by user, then we translate that into shrink factor.
|
|
||||||
*/
|
|
||||||
int page;
|
|
||||||
int shrink;
|
|
||||||
|
|
||||||
/* Base image properties.
|
|
||||||
*/
|
|
||||||
JxlBasicInfo info;
|
|
||||||
JxlPixelFormat format;
|
|
||||||
size_t icc_size;
|
|
||||||
uint8_t *icc_data;
|
|
||||||
|
|
||||||
/* Decompress state.
|
|
||||||
*/
|
|
||||||
void *runner;
|
|
||||||
JxlDecoder *decoder;
|
|
||||||
|
|
||||||
/* Our input buffer.
|
|
||||||
*/
|
|
||||||
uint8_t input_buffer[INPUT_BUFFER_SIZE];
|
|
||||||
size_t bytes_in_buffer;
|
|
||||||
|
|
||||||
/* Number of errors reported during load -- use this to block load of
|
|
||||||
* corrupted images.
|
|
||||||
*/
|
|
||||||
int n_errors;
|
|
||||||
|
|
||||||
/* If we need to upsample tiles read from opj.
|
|
||||||
*/
|
|
||||||
gboolean upsample;
|
|
||||||
|
|
||||||
/* If we need to do ycc->rgb conversion on load.
|
|
||||||
*/
|
|
||||||
gboolean ycc_to_rgb;
|
|
||||||
} VipsForeignLoadJxl;
|
|
||||||
|
|
||||||
typedef VipsForeignLoadClass VipsForeignLoadJxlClass;
|
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadJxl, vips_foreign_load_jxl,
|
|
||||||
VIPS_TYPE_FOREIGN_LOAD );
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_dispose( GObject *gobject )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) gobject;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_dispose:\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
|
|
||||||
VIPS_FREEF( JxlDecoderDestroy, jxl->decoder );
|
|
||||||
VIPS_FREE( jxl->icc_data );
|
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_foreign_load_jxl_parent_class )->
|
|
||||||
dispose( gobject );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_error( VipsForeignLoadJxl *jxl, const char *details )
|
|
||||||
{
|
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
|
||||||
|
|
||||||
/* TODO ... jxl has no way to get error messages at the moment.
|
|
||||||
*/
|
|
||||||
vips_error( class->nickname, "error %s", details );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_build:\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
jxl->runner = JxlThreadParallelRunnerCreate( NULL,
|
|
||||||
vips_concurrency_get() );
|
|
||||||
jxl->decoder = JxlDecoderCreate( NULL );
|
|
||||||
|
|
||||||
if( JxlDecoderSubscribeEvents( jxl->decoder,
|
|
||||||
JXL_DEC_COLOR_ENCODING |
|
|
||||||
JXL_DEC_BASIC_INFO |
|
|
||||||
JXL_DEC_FULL_IMAGE ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl, "JxlDecoderSubscribeEvents" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
if( JxlDecoderSetParallelRunner( jxl->decoder,
|
|
||||||
JxlThreadParallelRunner, jxl->runner ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderSetParallelRunner" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
vips_foreign_load_jxl_is_a_source( VipsSource *source )
|
|
||||||
{
|
|
||||||
const unsigned char *p;
|
|
||||||
JxlSignature sig;
|
|
||||||
|
|
||||||
return( (p = vips_source_sniff( source, 12 )) &&
|
|
||||||
(sig = JxlSignatureCheck( p, 12 )) == JXL_SIG_CODESTREAM );
|
|
||||||
}
|
|
||||||
|
|
||||||
static VipsForeignFlags
|
|
||||||
vips_foreign_load_jxl_get_flags( VipsForeignLoad *load )
|
|
||||||
{
|
|
||||||
return( VIPS_FOREIGN_PARTIAL );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_fill_input( VipsForeignLoadJxl *jxl,
|
|
||||||
size_t bytes_remaining )
|
|
||||||
{
|
|
||||||
gint64 bytes_read;
|
|
||||||
|
|
||||||
memcpy( jxl->input_buffer,
|
|
||||||
jxl->input_buffer + jxl->bytes_in_buffer - bytes_remaining,
|
|
||||||
bytes_remaining );
|
|
||||||
bytes_read = vips_source_read( jxl->source,
|
|
||||||
jxl->input_buffer + bytes_remaining,
|
|
||||||
INPUT_BUFFER_SIZE - bytes_remaining );
|
|
||||||
/* Read error, or unexpected end of input.
|
|
||||||
*/
|
|
||||||
if( bytes_read <= 0 )
|
|
||||||
return( -1 );
|
|
||||||
jxl->bytes_in_buffer = bytes_read + bytes_remaining;
|
|
||||||
|
|
||||||
#ifdef DEBUG_VERBOSE
|
|
||||||
printf( "vips_foreign_load_jxl_fill_input: %zd bytes read\n",
|
|
||||||
bytes_read );
|
|
||||||
#endif /*DEBUG_VERBOSE*/
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_print_status( JxlDecoderStatus status )
|
|
||||||
{
|
|
||||||
switch( status ) {
|
|
||||||
case JXL_DEC_SUCCESS:
|
|
||||||
printf( "JXL_DEC_SUCCESS\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_ERROR:
|
|
||||||
printf( "JXL_DEC_ERROR\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_NEED_MORE_INPUT:
|
|
||||||
printf( "JXL_DEC_NEED_MORE_INPUT\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_NEED_PREVIEW_OUT_BUFFER:
|
|
||||||
printf( "JXL_DEC_NEED_PREVIEW_OUT_BUFFER\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_NEED_DC_OUT_BUFFER:
|
|
||||||
printf( "JXL_DEC_NEED_DC_OUT_BUFFER\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
|
|
||||||
printf( "JXL_DEC_NEED_IMAGE_OUT_BUFFER\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_JPEG_NEED_MORE_OUTPUT:
|
|
||||||
printf( "JXL_DEC_JPEG_NEED_MORE_OUTPUT\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_BASIC_INFO:
|
|
||||||
printf( "JXL_DEC_BASIC_INFO\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_EXTENSIONS:
|
|
||||||
printf( "JXL_DEC_EXTENSIONS\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_COLOR_ENCODING:
|
|
||||||
printf( "JXL_DEC_COLOR_ENCODING\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_PREVIEW_IMAGE:
|
|
||||||
printf( "JXL_DEC_PREVIEW_IMAGE\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_FRAME:
|
|
||||||
printf( "JXL_DEC_FRAME\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_DC_IMAGE:
|
|
||||||
printf( "JXL_DEC_DC_IMAGE\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_FULL_IMAGE:
|
|
||||||
printf( "JXL_DEC_FULL_IMAGE\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_JPEG_RECONSTRUCTION:
|
|
||||||
printf( "JXL_DEC_JPEG_RECONSTRUCTION\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf( "JXL_DEC_<unknown>\n" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_print_info( JxlBasicInfo *info )
|
|
||||||
{
|
|
||||||
printf( "JxlBasicInfo:\n" );
|
|
||||||
printf( " have_container = %d\n", info->have_container );
|
|
||||||
printf( " xsize = %d\n", info->xsize );
|
|
||||||
printf( " ysize = %d\n", info->ysize );
|
|
||||||
printf( " bits_per_sample = %d\n", info->bits_per_sample );
|
|
||||||
printf( " exponent_bits_per_sample = %d\n",
|
|
||||||
info->exponent_bits_per_sample );
|
|
||||||
printf( " intensity_target = %g\n", info->intensity_target );
|
|
||||||
printf( " min_nits = %g\n", info->min_nits );
|
|
||||||
printf( " relative_to_max_display = %d\n",
|
|
||||||
info->relative_to_max_display );
|
|
||||||
printf( " linear_below = %g\n", info->linear_below );
|
|
||||||
printf( " uses_original_profile = %d\n",
|
|
||||||
info->uses_original_profile );
|
|
||||||
printf( " have_preview = %d\n", info->have_preview );
|
|
||||||
printf( " have_animation = %d\n", info->have_animation );
|
|
||||||
printf( " orientation = %d\n", info->orientation );
|
|
||||||
printf( " num_color_channels = %d\n", info->num_color_channels );
|
|
||||||
printf( " num_extra_channels = %d\n", info->num_extra_channels );
|
|
||||||
printf( " alpha_bits = %d\n", info->alpha_bits );
|
|
||||||
printf( " alpha_exponent_bits = %d\n", info->alpha_exponent_bits );
|
|
||||||
printf( " alpha_premultiplied = %d\n", info->alpha_premultiplied );
|
|
||||||
printf( " preview.xsize = %d\n", info->preview.xsize );
|
|
||||||
printf( " preview.ysize = %d\n", info->preview.ysize );
|
|
||||||
printf( " animation.tps_numerator = %d\n",
|
|
||||||
info->animation.tps_numerator );
|
|
||||||
printf( " animation.tps_denominator = %d\n",
|
|
||||||
info->animation.tps_denominator );
|
|
||||||
printf( " animation.num_loops = %d\n", info->animation.num_loops );
|
|
||||||
printf( " animation.have_timecodes = %d\n",
|
|
||||||
info->animation.have_timecodes );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_print_format( JxlPixelFormat *format )
|
|
||||||
{
|
|
||||||
printf( "JxlPixelFormat:\n" );
|
|
||||||
printf( " data_type = " );
|
|
||||||
switch( format->data_type ) {
|
|
||||||
case JXL_TYPE_UINT8:
|
|
||||||
printf( "JXL_TYPE_UINT8" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT16:
|
|
||||||
printf( "JXL_TYPE_UINT16" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
printf( "JXL_TYPE_UINT32" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_FLOAT:
|
|
||||||
printf( "JXL_TYPE_FLOAT" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf( "(unknown)" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf( "\n" );
|
|
||||||
printf( " num_channels = %d\n", format->num_channels );
|
|
||||||
printf( " endianness = %d\n", format->endianness );
|
|
||||||
printf( " align = %zd\n", format->align );
|
|
||||||
}
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
static JxlDecoderStatus
|
|
||||||
vips_foreign_load_jxl_process( VipsForeignLoadJxl *jxl )
|
|
||||||
{
|
|
||||||
JxlDecoderStatus status;
|
|
||||||
|
|
||||||
while( (status = JxlDecoderProcessInput( jxl->decoder )) ==
|
|
||||||
JXL_DEC_NEED_MORE_INPUT ) {
|
|
||||||
size_t bytes_remaining;
|
|
||||||
|
|
||||||
bytes_remaining = JxlDecoderReleaseInput( jxl->decoder );
|
|
||||||
if( vips_foreign_load_jxl_fill_input( jxl, bytes_remaining ) )
|
|
||||||
return( JXL_DEC_ERROR );
|
|
||||||
|
|
||||||
JxlDecoderSetInput( jxl->decoder,
|
|
||||||
jxl->input_buffer, jxl->bytes_in_buffer );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_process: seen " );
|
|
||||||
vips_foreign_load_jxl_print_status( status );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
return( status );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_set_header( VipsForeignLoadJxl *jxl, VipsImage *out )
|
|
||||||
{
|
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
|
||||||
|
|
||||||
VipsBandFormat format;
|
|
||||||
VipsInterpretation interpretation;
|
|
||||||
|
|
||||||
if( jxl->info.xsize >= VIPS_MAX_COORD ||
|
|
||||||
jxl->info.ysize >= VIPS_MAX_COORD ) {
|
|
||||||
vips_error( class->nickname,
|
|
||||||
"%s", _( "image size out of bounds" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( jxl->format.data_type ) {
|
|
||||||
case JXL_TYPE_UINT8:
|
|
||||||
format = VIPS_FORMAT_UCHAR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT16:
|
|
||||||
format = VIPS_FORMAT_USHORT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
format = VIPS_FORMAT_UINT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_FLOAT:
|
|
||||||
format = VIPS_FORMAT_FLOAT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
g_assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( jxl->info.num_color_channels ) {
|
|
||||||
case 1:
|
|
||||||
switch( format ) {
|
|
||||||
case VIPS_FORMAT_UCHAR:
|
|
||||||
interpretation = VIPS_INTERPRETATION_B_W;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
|
||||||
case VIPS_FORMAT_UINT:
|
|
||||||
interpretation = VIPS_INTERPRETATION_GREY16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
interpretation = VIPS_INTERPRETATION_B_W;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
switch( format ) {
|
|
||||||
case VIPS_FORMAT_UCHAR:
|
|
||||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
|
||||||
case VIPS_FORMAT_UINT:
|
|
||||||
interpretation = VIPS_INTERPRETATION_RGB16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
|
||||||
interpretation = VIPS_INTERPRETATION_scRGB;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
interpretation = VIPS_INTERPRETATION_sRGB;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
interpretation = VIPS_INTERPRETATION_MULTIBAND;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Even though this is a full image reader, we hint thinstrip since
|
|
||||||
* we are quite happy serving that if anything downstream
|
|
||||||
* would like it.
|
|
||||||
*/
|
|
||||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
|
||||||
|
|
||||||
vips_image_init_fields( out,
|
|
||||||
jxl->info.xsize, jxl->info.ysize, jxl->format.num_channels,
|
|
||||||
format, VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
|
|
||||||
|
|
||||||
if( jxl->icc_data &&
|
|
||||||
jxl->icc_size > 0 ) {
|
|
||||||
vips_image_set_blob( out, VIPS_META_ICC_NAME,
|
|
||||||
(VipsCallbackFn) vips_area_free_cb,
|
|
||||||
jxl->icc_data, jxl->icc_size );
|
|
||||||
jxl->icc_data = NULL;
|
|
||||||
jxl->icc_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vips_image_set_int( out,
|
|
||||||
VIPS_META_ORIENTATION, jxl->info.orientation );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_header( VipsForeignLoad *load )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
|
||||||
|
|
||||||
JxlDecoderStatus status;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_header:\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
if( vips_source_rewind( jxl->source ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( vips_foreign_load_jxl_fill_input( jxl, 0 ) )
|
|
||||||
return( -1 );
|
|
||||||
JxlDecoderSetInput( jxl->decoder,
|
|
||||||
jxl->input_buffer, jxl->bytes_in_buffer );
|
|
||||||
|
|
||||||
/* Read to the end of the header.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
switch( (status = vips_foreign_load_jxl_process( jxl )) ) {
|
|
||||||
case JXL_DEC_ERROR:
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderProcessInput" );
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
case JXL_DEC_BASIC_INFO:
|
|
||||||
if( JxlDecoderGetBasicInfo( jxl->decoder,
|
|
||||||
&jxl->info ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderGetBasicInfo" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
#ifdef DEBUG
|
|
||||||
vips_foreign_load_jxl_print_info( &jxl->info );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
/* Pick a pixel format to decode to.
|
|
||||||
*/
|
|
||||||
jxl->format.num_channels =
|
|
||||||
jxl->info.num_color_channels +
|
|
||||||
jxl->info.num_extra_channels;
|
|
||||||
if( jxl->info.exponent_bits_per_sample > 0 ||
|
|
||||||
jxl->info.alpha_exponent_bits > 0 )
|
|
||||||
jxl->format.data_type = JXL_TYPE_FLOAT;
|
|
||||||
else if( jxl->info.bits_per_sample > 16 )
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT32;
|
|
||||||
else if( jxl->info.bits_per_sample > 8 )
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT16;
|
|
||||||
else
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT8;
|
|
||||||
jxl->format.endianness = JXL_NATIVE_ENDIAN;
|
|
||||||
jxl->format.align = 0;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
vips_foreign_load_jxl_print_format( &jxl->format );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_COLOR_ENCODING:
|
|
||||||
if( JxlDecoderGetICCProfileSize( jxl->decoder,
|
|
||||||
&jxl->format,
|
|
||||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
|
||||||
&jxl->icc_size ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderGetICCProfileSize" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_header: "
|
|
||||||
"%zd byte profile\n", jxl->icc_size );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
if( !(jxl->icc_data = vips_malloc( NULL,
|
|
||||||
jxl->icc_size )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( JxlDecoderGetColorAsICCProfile( jxl->decoder,
|
|
||||||
&jxl->format,
|
|
||||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
|
||||||
jxl->icc_data, jxl->icc_size ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderGetColorAsICCProfile" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* JXL_DEC_COLOR_ENCODING is always the last status signal before
|
|
||||||
* pixel decoding starts.
|
|
||||||
*/
|
|
||||||
} while( status != JXL_DEC_COLOR_ENCODING );
|
|
||||||
|
|
||||||
if( vips_foreign_load_jxl_set_header( jxl, load->out ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
VIPS_SETSTR( load->out->filename,
|
|
||||||
vips_connection_filename( VIPS_CONNECTION( jxl->source ) ) );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_load( VipsForeignLoad *load )
|
|
||||||
{
|
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
|
||||||
VipsImage **t = (VipsImage **)
|
|
||||||
vips_object_local_array( VIPS_OBJECT( load ), 3 );
|
|
||||||
|
|
||||||
size_t buffer_size;
|
|
||||||
JxlDecoderStatus status;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_load_jxl_load:\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
t[0] = vips_image_new();
|
|
||||||
if( vips_foreign_load_jxl_set_header( jxl, t[0] ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Read to the end of the image.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
switch( (status = vips_foreign_load_jxl_process( jxl )) ) {
|
|
||||||
case JXL_DEC_ERROR:
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderProcessInput" );
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
|
|
||||||
if( vips_image_write_prepare( t[0] ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( JxlDecoderImageOutBufferSize( jxl->decoder,
|
|
||||||
&jxl->format,
|
|
||||||
&buffer_size ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderImageOutBufferSize" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
if( buffer_size !=
|
|
||||||
VIPS_IMAGE_SIZEOF_IMAGE( t[0] ) ) {
|
|
||||||
vips_error( class->nickname,
|
|
||||||
"%s", _( "bad buffer size" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
if( JxlDecoderSetImageOutBuffer( jxl->decoder,
|
|
||||||
&jxl->format,
|
|
||||||
VIPS_IMAGE_ADDR( t[0], 0, 0 ),
|
|
||||||
VIPS_IMAGE_SIZEOF_IMAGE( t[0] ) ) ) {
|
|
||||||
vips_foreign_load_jxl_error( jxl,
|
|
||||||
"JxlDecoderSetImageOutBuffer" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_DEC_FULL_IMAGE:
|
|
||||||
/* Image decoded.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while( status != JXL_DEC_SUCCESS );
|
|
||||||
|
|
||||||
if( vips_image_write( t[0], load->real ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_class_init( VipsForeignLoadJxlClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
|
||||||
|
|
||||||
gobject_class->dispose = vips_foreign_load_jxl_dispose;
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlload_base";
|
|
||||||
object_class->description = _( "load JPEG-XL image" );
|
|
||||||
object_class->build = vips_foreign_load_jxl_build;
|
|
||||||
|
|
||||||
load_class->get_flags = vips_foreign_load_jxl_get_flags;
|
|
||||||
load_class->header = vips_foreign_load_jxl_header;
|
|
||||||
load_class->load = vips_foreign_load_jxl_load;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_init( VipsForeignLoadJxl *jxl )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadJxlFile {
|
|
||||||
VipsForeignLoadJxl parent_object;
|
|
||||||
|
|
||||||
/* Filename for load.
|
|
||||||
*/
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
} VipsForeignLoadJxlFile;
|
|
||||||
|
|
||||||
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlFileClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignLoadJxlFile, vips_foreign_load_jxl_file,
|
|
||||||
vips_foreign_load_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_file_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
|
||||||
VipsForeignLoadJxlFile *file = (VipsForeignLoadJxlFile *) object;
|
|
||||||
|
|
||||||
if( file->filename &&
|
|
||||||
!(jxl->source = vips_source_new_from_file( file->filename )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_file_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *vips__jxl_suffs[] =
|
|
||||||
{ ".jxl", NULL };
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_is_a( const char *filename )
|
|
||||||
{
|
|
||||||
VipsSource *source;
|
|
||||||
gboolean result;
|
|
||||||
|
|
||||||
if( !(source = vips_source_new_from_file( filename )) )
|
|
||||||
return( FALSE );
|
|
||||||
result = vips_foreign_load_jxl_is_a_source( source );
|
|
||||||
VIPS_UNREF( source );
|
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_file_class_init( VipsForeignLoadJxlFileClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
|
||||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlload";
|
|
||||||
object_class->build = vips_foreign_load_jxl_file_build;
|
|
||||||
|
|
||||||
foreign_class->suffs = vips__jxl_suffs;
|
|
||||||
|
|
||||||
load_class->is_a = vips_foreign_load_jxl_is_a;
|
|
||||||
|
|
||||||
VIPS_ARG_STRING( class, "filename", 1,
|
|
||||||
_( "Filename" ),
|
|
||||||
_( "Filename to load from" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignLoadJxlFile, filename ),
|
|
||||||
NULL );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_file_init( VipsForeignLoadJxlFile *jxl )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadJxlBuffer {
|
|
||||||
VipsForeignLoadJxl parent_object;
|
|
||||||
|
|
||||||
/* Load from a buffer.
|
|
||||||
*/
|
|
||||||
VipsArea *buf;
|
|
||||||
|
|
||||||
} VipsForeignLoadJxlBuffer;
|
|
||||||
|
|
||||||
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlBufferClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignLoadJxlBuffer, vips_foreign_load_jxl_buffer,
|
|
||||||
vips_foreign_load_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_buffer_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
|
||||||
VipsForeignLoadJxlBuffer *buffer =
|
|
||||||
(VipsForeignLoadJxlBuffer *) object;
|
|
||||||
|
|
||||||
if( buffer->buf )
|
|
||||||
if( !(jxl->source = vips_source_new_from_memory(
|
|
||||||
VIPS_AREA( buffer->buf )->data,
|
|
||||||
VIPS_AREA( buffer->buf )->length )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_file_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
vips_foreign_load_jxl_buffer_is_a( const void *buf, size_t len )
|
|
||||||
{
|
|
||||||
VipsSource *source;
|
|
||||||
gboolean result;
|
|
||||||
|
|
||||||
if( !(source = vips_source_new_from_memory( buf, len )) )
|
|
||||||
return( FALSE );
|
|
||||||
result = vips_foreign_load_jxl_is_a_source( source );
|
|
||||||
VIPS_UNREF( source );
|
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_buffer_class_init( VipsForeignLoadJxlBufferClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlload_buffer";
|
|
||||||
object_class->build = vips_foreign_load_jxl_buffer_build;
|
|
||||||
|
|
||||||
load_class->is_a_buffer = vips_foreign_load_jxl_buffer_is_a;
|
|
||||||
|
|
||||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
|
||||||
_( "Buffer" ),
|
|
||||||
_( "Buffer to load from" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignLoadJxlBuffer, buf ),
|
|
||||||
VIPS_TYPE_BLOB );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_buffer_init( VipsForeignLoadJxlBuffer *buffer )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadJxlSource {
|
|
||||||
VipsForeignLoadJxl parent_object;
|
|
||||||
|
|
||||||
/* Load from a source.
|
|
||||||
*/
|
|
||||||
VipsSource *source;
|
|
||||||
|
|
||||||
} VipsForeignLoadJxlSource;
|
|
||||||
|
|
||||||
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlSourceClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignLoadJxlSource, vips_foreign_load_jxl_source,
|
|
||||||
vips_foreign_load_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_jxl_source_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
|
||||||
VipsForeignLoadJxlSource *source =
|
|
||||||
(VipsForeignLoadJxlSource *) object;
|
|
||||||
|
|
||||||
if( source->source ) {
|
|
||||||
jxl->source = source->source;
|
|
||||||
g_object_ref( jxl->source );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS(
|
|
||||||
vips_foreign_load_jxl_source_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_source_class_init( VipsForeignLoadJxlSourceClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlload_source";
|
|
||||||
object_class->build = vips_foreign_load_jxl_source_build;
|
|
||||||
|
|
||||||
load_class->is_a_source = vips_foreign_load_jxl_is_a_source;
|
|
||||||
|
|
||||||
VIPS_ARG_OBJECT( class, "source", 1,
|
|
||||||
_( "Source" ),
|
|
||||||
_( "Source to load from" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignLoadJxlSource, source ),
|
|
||||||
VIPS_TYPE_SOURCE );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_jxl_source_init(
|
|
||||||
VipsForeignLoadJxlSource *jxl )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /*HAVE_LIBJXL*/
|
|
@ -31,91 +31,890 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define DEBUG_VERBOSE
|
||||||
|
#define DEBUG
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
#include <vips/intl.h>
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
#include <vips/debug.h>
|
||||||
|
#include <vips/internal.h>
|
||||||
|
|
||||||
/**
|
#ifdef HAVE_LIBJXL
|
||||||
* vips_jxlload:
|
|
||||||
* @filename: file to load
|
#include <jxl/decode.h>
|
||||||
* @out: (out): decompressed image
|
#include <jxl/thread_parallel_runner.h>
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
|
#include "pforeign.h"
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
*
|
*
|
||||||
* Read a JPEG-XL image.
|
* - add metadata support
|
||||||
*
|
*
|
||||||
* The JPEG-XL loader and saver are experimental features and may change
|
* - add animation support
|
||||||
* in future libvips versions.
|
|
||||||
*
|
*
|
||||||
* See also: vips_image_new_from_file().
|
* - add "shrink" option to read out 8x shrunk image?
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -1 on error.
|
* - fix scRGB gamma
|
||||||
*/
|
*/
|
||||||
int
|
|
||||||
vips_jxlload( const char *filename, VipsImage **out, ... )
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, out );
|
#define INPUT_BUFFER_SIZE (4096)
|
||||||
result = vips_call_split( "jxlload", ap, filename, out );
|
|
||||||
va_end( ap );
|
typedef struct _VipsForeignLoadJxl {
|
||||||
|
VipsForeignLoad parent_object;
|
||||||
|
|
||||||
|
/* Source to load from (set by subclasses).
|
||||||
|
*/
|
||||||
|
VipsSource *source;
|
||||||
|
|
||||||
|
/* Page set by user, then we translate that into shrink factor.
|
||||||
|
*/
|
||||||
|
int page;
|
||||||
|
int shrink;
|
||||||
|
|
||||||
|
/* Base image properties.
|
||||||
|
*/
|
||||||
|
JxlBasicInfo info;
|
||||||
|
JxlPixelFormat format;
|
||||||
|
size_t icc_size;
|
||||||
|
uint8_t *icc_data;
|
||||||
|
|
||||||
|
/* Decompress state.
|
||||||
|
*/
|
||||||
|
void *runner;
|
||||||
|
JxlDecoder *decoder;
|
||||||
|
|
||||||
|
/* Our input buffer.
|
||||||
|
*/
|
||||||
|
uint8_t input_buffer[INPUT_BUFFER_SIZE];
|
||||||
|
size_t bytes_in_buffer;
|
||||||
|
|
||||||
|
/* Number of errors reported during load -- use this to block load of
|
||||||
|
* corrupted images.
|
||||||
|
*/
|
||||||
|
int n_errors;
|
||||||
|
|
||||||
|
/* If we need to upsample tiles read from opj.
|
||||||
|
*/
|
||||||
|
gboolean upsample;
|
||||||
|
|
||||||
|
/* If we need to do ycc->rgb conversion on load.
|
||||||
|
*/
|
||||||
|
gboolean ycc_to_rgb;
|
||||||
|
} VipsForeignLoadJxl;
|
||||||
|
|
||||||
|
typedef VipsForeignLoadClass VipsForeignLoadJxlClass;
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadJxl, vips_foreign_load_jxl,
|
||||||
|
VIPS_TYPE_FOREIGN_LOAD );
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_dispose( GObject *gobject )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) gobject;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_dispose:\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
|
||||||
|
VIPS_FREEF( JxlDecoderDestroy, jxl->decoder );
|
||||||
|
VIPS_FREE( jxl->icc_data );
|
||||||
|
|
||||||
|
G_OBJECT_CLASS( vips_foreign_load_jxl_parent_class )->
|
||||||
|
dispose( gobject );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_error( VipsForeignLoadJxl *jxl, const char *details )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
||||||
|
|
||||||
|
/* TODO ... jxl has no way to get error messages at the moment.
|
||||||
|
*/
|
||||||
|
vips_error( class->nickname, "error %s", details );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_build:\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
jxl->runner = JxlThreadParallelRunnerCreate( NULL,
|
||||||
|
vips_concurrency_get() );
|
||||||
|
jxl->decoder = JxlDecoderCreate( NULL );
|
||||||
|
|
||||||
|
if( JxlDecoderSubscribeEvents( jxl->decoder,
|
||||||
|
JXL_DEC_COLOR_ENCODING |
|
||||||
|
JXL_DEC_BASIC_INFO |
|
||||||
|
JXL_DEC_FULL_IMAGE ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl, "JxlDecoderSubscribeEvents" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
if( JxlDecoderSetParallelRunner( jxl->decoder,
|
||||||
|
JxlThreadParallelRunner, jxl->runner ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderSetParallelRunner" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_jxl_is_a_source( VipsSource *source )
|
||||||
|
{
|
||||||
|
const unsigned char *p;
|
||||||
|
JxlSignature sig;
|
||||||
|
|
||||||
|
return( (p = vips_source_sniff( source, 12 )) &&
|
||||||
|
(sig = JxlSignatureCheck( p, 12 )) == JXL_SIG_CODESTREAM );
|
||||||
|
}
|
||||||
|
|
||||||
|
static VipsForeignFlags
|
||||||
|
vips_foreign_load_jxl_get_flags( VipsForeignLoad *load )
|
||||||
|
{
|
||||||
|
return( VIPS_FOREIGN_PARTIAL );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_fill_input( VipsForeignLoadJxl *jxl,
|
||||||
|
size_t bytes_remaining )
|
||||||
|
{
|
||||||
|
gint64 bytes_read;
|
||||||
|
|
||||||
|
memcpy( jxl->input_buffer,
|
||||||
|
jxl->input_buffer + jxl->bytes_in_buffer - bytes_remaining,
|
||||||
|
bytes_remaining );
|
||||||
|
bytes_read = vips_source_read( jxl->source,
|
||||||
|
jxl->input_buffer + bytes_remaining,
|
||||||
|
INPUT_BUFFER_SIZE - bytes_remaining );
|
||||||
|
/* Read error, or unexpected end of input.
|
||||||
|
*/
|
||||||
|
if( bytes_read <= 0 )
|
||||||
|
return( -1 );
|
||||||
|
jxl->bytes_in_buffer = bytes_read + bytes_remaining;
|
||||||
|
|
||||||
|
#ifdef DEBUG_VERBOSE
|
||||||
|
printf( "vips_foreign_load_jxl_fill_input: %zd bytes read\n",
|
||||||
|
bytes_read );
|
||||||
|
#endif /*DEBUG_VERBOSE*/
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_print_status( JxlDecoderStatus status )
|
||||||
|
{
|
||||||
|
switch( status ) {
|
||||||
|
case JXL_DEC_SUCCESS:
|
||||||
|
printf( "JXL_DEC_SUCCESS\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_ERROR:
|
||||||
|
printf( "JXL_DEC_ERROR\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_NEED_MORE_INPUT:
|
||||||
|
printf( "JXL_DEC_NEED_MORE_INPUT\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_NEED_PREVIEW_OUT_BUFFER:
|
||||||
|
printf( "JXL_DEC_NEED_PREVIEW_OUT_BUFFER\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_NEED_DC_OUT_BUFFER:
|
||||||
|
printf( "JXL_DEC_NEED_DC_OUT_BUFFER\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
|
||||||
|
printf( "JXL_DEC_NEED_IMAGE_OUT_BUFFER\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_JPEG_NEED_MORE_OUTPUT:
|
||||||
|
printf( "JXL_DEC_JPEG_NEED_MORE_OUTPUT\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_BASIC_INFO:
|
||||||
|
printf( "JXL_DEC_BASIC_INFO\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_EXTENSIONS:
|
||||||
|
printf( "JXL_DEC_EXTENSIONS\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_COLOR_ENCODING:
|
||||||
|
printf( "JXL_DEC_COLOR_ENCODING\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_PREVIEW_IMAGE:
|
||||||
|
printf( "JXL_DEC_PREVIEW_IMAGE\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_FRAME:
|
||||||
|
printf( "JXL_DEC_FRAME\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_DC_IMAGE:
|
||||||
|
printf( "JXL_DEC_DC_IMAGE\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_FULL_IMAGE:
|
||||||
|
printf( "JXL_DEC_FULL_IMAGE\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_JPEG_RECONSTRUCTION:
|
||||||
|
printf( "JXL_DEC_JPEG_RECONSTRUCTION\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf( "JXL_DEC_<unknown>\n" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_print_info( JxlBasicInfo *info )
|
||||||
|
{
|
||||||
|
printf( "JxlBasicInfo:\n" );
|
||||||
|
printf( " have_container = %d\n", info->have_container );
|
||||||
|
printf( " xsize = %d\n", info->xsize );
|
||||||
|
printf( " ysize = %d\n", info->ysize );
|
||||||
|
printf( " bits_per_sample = %d\n", info->bits_per_sample );
|
||||||
|
printf( " exponent_bits_per_sample = %d\n",
|
||||||
|
info->exponent_bits_per_sample );
|
||||||
|
printf( " intensity_target = %g\n", info->intensity_target );
|
||||||
|
printf( " min_nits = %g\n", info->min_nits );
|
||||||
|
printf( " relative_to_max_display = %d\n",
|
||||||
|
info->relative_to_max_display );
|
||||||
|
printf( " linear_below = %g\n", info->linear_below );
|
||||||
|
printf( " uses_original_profile = %d\n",
|
||||||
|
info->uses_original_profile );
|
||||||
|
printf( " have_preview = %d\n", info->have_preview );
|
||||||
|
printf( " have_animation = %d\n", info->have_animation );
|
||||||
|
printf( " orientation = %d\n", info->orientation );
|
||||||
|
printf( " num_color_channels = %d\n", info->num_color_channels );
|
||||||
|
printf( " num_extra_channels = %d\n", info->num_extra_channels );
|
||||||
|
printf( " alpha_bits = %d\n", info->alpha_bits );
|
||||||
|
printf( " alpha_exponent_bits = %d\n", info->alpha_exponent_bits );
|
||||||
|
printf( " alpha_premultiplied = %d\n", info->alpha_premultiplied );
|
||||||
|
printf( " preview.xsize = %d\n", info->preview.xsize );
|
||||||
|
printf( " preview.ysize = %d\n", info->preview.ysize );
|
||||||
|
printf( " animation.tps_numerator = %d\n",
|
||||||
|
info->animation.tps_numerator );
|
||||||
|
printf( " animation.tps_denominator = %d\n",
|
||||||
|
info->animation.tps_denominator );
|
||||||
|
printf( " animation.num_loops = %d\n", info->animation.num_loops );
|
||||||
|
printf( " animation.have_timecodes = %d\n",
|
||||||
|
info->animation.have_timecodes );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_print_format( JxlPixelFormat *format )
|
||||||
|
{
|
||||||
|
printf( "JxlPixelFormat:\n" );
|
||||||
|
printf( " data_type = " );
|
||||||
|
switch( format->data_type ) {
|
||||||
|
case JXL_TYPE_UINT8:
|
||||||
|
printf( "JXL_TYPE_UINT8" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT16:
|
||||||
|
printf( "JXL_TYPE_UINT16" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT32:
|
||||||
|
printf( "JXL_TYPE_UINT32" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_FLOAT:
|
||||||
|
printf( "JXL_TYPE_FLOAT" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf( "(unknown)" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf( "\n" );
|
||||||
|
printf( " num_channels = %d\n", format->num_channels );
|
||||||
|
printf( " endianness = %d\n", format->endianness );
|
||||||
|
printf( " align = %zd\n", format->align );
|
||||||
|
}
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
static JxlDecoderStatus
|
||||||
|
vips_foreign_load_jxl_process( VipsForeignLoadJxl *jxl )
|
||||||
|
{
|
||||||
|
JxlDecoderStatus status;
|
||||||
|
|
||||||
|
while( (status = JxlDecoderProcessInput( jxl->decoder )) ==
|
||||||
|
JXL_DEC_NEED_MORE_INPUT ) {
|
||||||
|
size_t bytes_remaining;
|
||||||
|
|
||||||
|
bytes_remaining = JxlDecoderReleaseInput( jxl->decoder );
|
||||||
|
if( vips_foreign_load_jxl_fill_input( jxl, bytes_remaining ) )
|
||||||
|
return( JXL_DEC_ERROR );
|
||||||
|
|
||||||
|
JxlDecoderSetInput( jxl->decoder,
|
||||||
|
jxl->input_buffer, jxl->bytes_in_buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_process: seen " );
|
||||||
|
vips_foreign_load_jxl_print_status( status );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
return( status );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_set_header( VipsForeignLoadJxl *jxl, VipsImage *out )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
||||||
|
|
||||||
|
VipsBandFormat format;
|
||||||
|
VipsInterpretation interpretation;
|
||||||
|
|
||||||
|
if( jxl->info.xsize >= VIPS_MAX_COORD ||
|
||||||
|
jxl->info.ysize >= VIPS_MAX_COORD ) {
|
||||||
|
vips_error( class->nickname,
|
||||||
|
"%s", _( "image size out of bounds" ) );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( jxl->format.data_type ) {
|
||||||
|
case JXL_TYPE_UINT8:
|
||||||
|
format = VIPS_FORMAT_UCHAR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT16:
|
||||||
|
format = VIPS_FORMAT_USHORT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT32:
|
||||||
|
format = VIPS_FORMAT_UINT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_FLOAT:
|
||||||
|
format = VIPS_FORMAT_FLOAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( jxl->info.num_color_channels ) {
|
||||||
|
case 1:
|
||||||
|
switch( format ) {
|
||||||
|
case VIPS_FORMAT_UCHAR:
|
||||||
|
interpretation = VIPS_INTERPRETATION_B_W;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_USHORT:
|
||||||
|
case VIPS_FORMAT_UINT:
|
||||||
|
interpretation = VIPS_INTERPRETATION_GREY16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
interpretation = VIPS_INTERPRETATION_B_W;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
switch( format ) {
|
||||||
|
case VIPS_FORMAT_UCHAR:
|
||||||
|
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_USHORT:
|
||||||
|
case VIPS_FORMAT_UINT:
|
||||||
|
interpretation = VIPS_INTERPRETATION_RGB16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_FLOAT:
|
||||||
|
interpretation = VIPS_INTERPRETATION_scRGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
interpretation = VIPS_INTERPRETATION_sRGB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
interpretation = VIPS_INTERPRETATION_MULTIBAND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Even though this is a full image reader, we hint thinstrip since
|
||||||
|
* we are quite happy serving that if anything downstream
|
||||||
|
* would like it.
|
||||||
|
*/
|
||||||
|
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
||||||
|
|
||||||
|
vips_image_init_fields( out,
|
||||||
|
jxl->info.xsize, jxl->info.ysize, jxl->format.num_channels,
|
||||||
|
format, VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
|
||||||
|
|
||||||
|
if( jxl->icc_data &&
|
||||||
|
jxl->icc_size > 0 ) {
|
||||||
|
vips_image_set_blob( out, VIPS_META_ICC_NAME,
|
||||||
|
(VipsCallbackFn) vips_area_free_cb,
|
||||||
|
jxl->icc_data, jxl->icc_size );
|
||||||
|
jxl->icc_data = NULL;
|
||||||
|
jxl->icc_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vips_image_set_int( out,
|
||||||
|
VIPS_META_ORIENTATION, jxl->info.orientation );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_header( VipsForeignLoad *load )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
||||||
|
|
||||||
|
JxlDecoderStatus status;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_header:\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
if( vips_source_rewind( jxl->source ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( vips_foreign_load_jxl_fill_input( jxl, 0 ) )
|
||||||
|
return( -1 );
|
||||||
|
JxlDecoderSetInput( jxl->decoder,
|
||||||
|
jxl->input_buffer, jxl->bytes_in_buffer );
|
||||||
|
|
||||||
|
/* Read to the end of the header.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
switch( (status = vips_foreign_load_jxl_process( jxl )) ) {
|
||||||
|
case JXL_DEC_ERROR:
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderProcessInput" );
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
case JXL_DEC_BASIC_INFO:
|
||||||
|
if( JxlDecoderGetBasicInfo( jxl->decoder,
|
||||||
|
&jxl->info ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderGetBasicInfo" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_foreign_load_jxl_print_info( &jxl->info );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
/* Pick a pixel format to decode to.
|
||||||
|
*/
|
||||||
|
jxl->format.num_channels =
|
||||||
|
jxl->info.num_color_channels +
|
||||||
|
jxl->info.num_extra_channels;
|
||||||
|
if( jxl->info.exponent_bits_per_sample > 0 ||
|
||||||
|
jxl->info.alpha_exponent_bits > 0 )
|
||||||
|
jxl->format.data_type = JXL_TYPE_FLOAT;
|
||||||
|
else if( jxl->info.bits_per_sample > 16 )
|
||||||
|
jxl->format.data_type = JXL_TYPE_UINT32;
|
||||||
|
else if( jxl->info.bits_per_sample > 8 )
|
||||||
|
jxl->format.data_type = JXL_TYPE_UINT16;
|
||||||
|
else
|
||||||
|
jxl->format.data_type = JXL_TYPE_UINT8;
|
||||||
|
jxl->format.endianness = JXL_NATIVE_ENDIAN;
|
||||||
|
jxl->format.align = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_foreign_load_jxl_print_format( &jxl->format );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_COLOR_ENCODING:
|
||||||
|
if( JxlDecoderGetICCProfileSize( jxl->decoder,
|
||||||
|
&jxl->format,
|
||||||
|
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||||
|
&jxl->icc_size ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderGetICCProfileSize" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_header: "
|
||||||
|
"%zd byte profile\n", jxl->icc_size );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
if( !(jxl->icc_data = vips_malloc( NULL,
|
||||||
|
jxl->icc_size )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( JxlDecoderGetColorAsICCProfile( jxl->decoder,
|
||||||
|
&jxl->format,
|
||||||
|
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||||
|
jxl->icc_data, jxl->icc_size ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderGetColorAsICCProfile" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* JXL_DEC_COLOR_ENCODING is always the last status signal before
|
||||||
|
* pixel decoding starts.
|
||||||
|
*/
|
||||||
|
} while( status != JXL_DEC_COLOR_ENCODING );
|
||||||
|
|
||||||
|
if( vips_foreign_load_jxl_set_header( jxl, load->out ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
VIPS_SETSTR( load->out->filename,
|
||||||
|
vips_connection_filename( VIPS_CONNECTION( jxl->source ) ) );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_load( VipsForeignLoad *load )
|
||||||
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
||||||
|
VipsImage **t = (VipsImage **)
|
||||||
|
vips_object_local_array( VIPS_OBJECT( load ), 3 );
|
||||||
|
|
||||||
|
size_t buffer_size;
|
||||||
|
JxlDecoderStatus status;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_jxl_load:\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
t[0] = vips_image_new();
|
||||||
|
if( vips_foreign_load_jxl_set_header( jxl, t[0] ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Read to the end of the image.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
switch( (status = vips_foreign_load_jxl_process( jxl )) ) {
|
||||||
|
case JXL_DEC_ERROR:
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderProcessInput" );
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
|
||||||
|
if( vips_image_write_prepare( t[0] ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( JxlDecoderImageOutBufferSize( jxl->decoder,
|
||||||
|
&jxl->format,
|
||||||
|
&buffer_size ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderImageOutBufferSize" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
if( buffer_size !=
|
||||||
|
VIPS_IMAGE_SIZEOF_IMAGE( t[0] ) ) {
|
||||||
|
vips_error( class->nickname,
|
||||||
|
"%s", _( "bad buffer size" ) );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
if( JxlDecoderSetImageOutBuffer( jxl->decoder,
|
||||||
|
&jxl->format,
|
||||||
|
VIPS_IMAGE_ADDR( t[0], 0, 0 ),
|
||||||
|
VIPS_IMAGE_SIZEOF_IMAGE( t[0] ) ) ) {
|
||||||
|
vips_foreign_load_jxl_error( jxl,
|
||||||
|
"JxlDecoderSetImageOutBuffer" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_DEC_FULL_IMAGE:
|
||||||
|
/* Image decoded.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while( status != JXL_DEC_SUCCESS );
|
||||||
|
|
||||||
|
if( vips_image_write( t[0], load->real ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_class_init( VipsForeignLoadJxlClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||||
|
|
||||||
|
gobject_class->dispose = vips_foreign_load_jxl_dispose;
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlload_base";
|
||||||
|
object_class->description = _( "load JPEG-XL image" );
|
||||||
|
object_class->build = vips_foreign_load_jxl_build;
|
||||||
|
|
||||||
|
load_class->get_flags = vips_foreign_load_jxl_get_flags;
|
||||||
|
load_class->header = vips_foreign_load_jxl_header;
|
||||||
|
load_class->load = vips_foreign_load_jxl_load;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_init( VipsForeignLoadJxl *jxl )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignLoadJxlFile {
|
||||||
|
VipsForeignLoadJxl parent_object;
|
||||||
|
|
||||||
|
/* Filename for load.
|
||||||
|
*/
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
} VipsForeignLoadJxlFile;
|
||||||
|
|
||||||
|
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlFileClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignLoadJxlFile, vips_foreign_load_jxl_file,
|
||||||
|
vips_foreign_load_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_file_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
||||||
|
VipsForeignLoadJxlFile *file = (VipsForeignLoadJxlFile *) object;
|
||||||
|
|
||||||
|
if( file->filename &&
|
||||||
|
!(jxl->source = vips_source_new_from_file( file->filename )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_file_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *vips__jxl_suffs[] =
|
||||||
|
{ ".jxl", NULL };
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_is_a( const char *filename )
|
||||||
|
{
|
||||||
|
VipsSource *source;
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
if( !(source = vips_source_new_from_file( filename )) )
|
||||||
|
return( FALSE );
|
||||||
|
result = vips_foreign_load_jxl_is_a_source( source );
|
||||||
|
VIPS_UNREF( source );
|
||||||
|
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* vips_jxlload_buffer:
|
vips_foreign_load_jxl_file_class_init( VipsForeignLoadJxlFileClass *class )
|
||||||
* @buf: (array length=len) (element-type guint8): memory area to load
|
|
||||||
* @len: (type gsize): size of memory area
|
|
||||||
* @out: (out): image to write
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Exactly as vips_jxlload(), but read from a buffer.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_jxlload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
VipsBlob *blob;
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
int result;
|
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||||
|
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||||
|
|
||||||
/* We don't take a copy of the data or free it.
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlload";
|
||||||
|
object_class->build = vips_foreign_load_jxl_file_build;
|
||||||
|
|
||||||
|
foreign_class->suffs = vips__jxl_suffs;
|
||||||
|
|
||||||
|
load_class->is_a = vips_foreign_load_jxl_is_a;
|
||||||
|
|
||||||
|
VIPS_ARG_STRING( class, "filename", 1,
|
||||||
|
_( "Filename" ),
|
||||||
|
_( "Filename to load from" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignLoadJxlFile, filename ),
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_file_init( VipsForeignLoadJxlFile *jxl )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignLoadJxlBuffer {
|
||||||
|
VipsForeignLoadJxl parent_object;
|
||||||
|
|
||||||
|
/* Load from a buffer.
|
||||||
*/
|
*/
|
||||||
blob = vips_blob_new( NULL, buf, len );
|
VipsArea *buf;
|
||||||
|
|
||||||
va_start( ap, out );
|
} VipsForeignLoadJxlBuffer;
|
||||||
result = vips_call_split( "jxlload_buffer", ap, blob, out );
|
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
vips_area_unref( VIPS_AREA( blob ) );
|
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlBufferClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignLoadJxlBuffer, vips_foreign_load_jxl_buffer,
|
||||||
|
vips_foreign_load_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_buffer_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
||||||
|
VipsForeignLoadJxlBuffer *buffer =
|
||||||
|
(VipsForeignLoadJxlBuffer *) object;
|
||||||
|
|
||||||
|
if( buffer->buf )
|
||||||
|
if( !(jxl->source = vips_source_new_from_memory(
|
||||||
|
VIPS_AREA( buffer->buf )->data,
|
||||||
|
VIPS_AREA( buffer->buf )->length )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_load_jxl_file_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_jxl_buffer_is_a( const void *buf, size_t len )
|
||||||
|
{
|
||||||
|
VipsSource *source;
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
if( !(source = vips_source_new_from_memory( buf, len )) )
|
||||||
|
return( FALSE );
|
||||||
|
result = vips_foreign_load_jxl_is_a_source( source );
|
||||||
|
VIPS_UNREF( source );
|
||||||
|
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* vips_jxlload_source:
|
vips_foreign_load_jxl_buffer_class_init( VipsForeignLoadJxlBufferClass *class )
|
||||||
* @source: source to load from
|
|
||||||
* @out: (out): decompressed image
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Exactly as vips_jxlload(), but read from a source.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_jxlload_source( VipsSource *source, VipsImage **out, ... )
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
int result;
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||||
|
|
||||||
va_start( ap, out );
|
gobject_class->set_property = vips_object_set_property;
|
||||||
result = vips_call_split( "jxlload_source", ap, source, out );
|
gobject_class->get_property = vips_object_get_property;
|
||||||
va_end( ap );
|
|
||||||
|
object_class->nickname = "jxlload_buffer";
|
||||||
|
object_class->build = vips_foreign_load_jxl_buffer_build;
|
||||||
|
|
||||||
|
load_class->is_a_buffer = vips_foreign_load_jxl_buffer_is_a;
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||||
|
_( "Buffer" ),
|
||||||
|
_( "Buffer to load from" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignLoadJxlBuffer, buf ),
|
||||||
|
VIPS_TYPE_BLOB );
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_buffer_init( VipsForeignLoadJxlBuffer *buffer )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignLoadJxlSource {
|
||||||
|
VipsForeignLoadJxl parent_object;
|
||||||
|
|
||||||
|
/* Load from a source.
|
||||||
|
*/
|
||||||
|
VipsSource *source;
|
||||||
|
|
||||||
|
} VipsForeignLoadJxlSource;
|
||||||
|
|
||||||
|
typedef VipsForeignLoadJxlClass VipsForeignLoadJxlSourceClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignLoadJxlSource, vips_foreign_load_jxl_source,
|
||||||
|
vips_foreign_load_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_jxl_source_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
||||||
|
VipsForeignLoadJxlSource *source =
|
||||||
|
(VipsForeignLoadJxlSource *) object;
|
||||||
|
|
||||||
|
if( source->source ) {
|
||||||
|
jxl->source = source->source;
|
||||||
|
g_object_ref( jxl->source );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS(
|
||||||
|
vips_foreign_load_jxl_source_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_source_class_init( VipsForeignLoadJxlSourceClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlload_source";
|
||||||
|
object_class->build = vips_foreign_load_jxl_source_build;
|
||||||
|
|
||||||
|
load_class->is_a_source = vips_foreign_load_jxl_is_a_source;
|
||||||
|
|
||||||
|
VIPS_ARG_OBJECT( class, "source", 1,
|
||||||
|
_( "Source" ),
|
||||||
|
_( "Source to load from" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignLoadJxlSource, source ),
|
||||||
|
VIPS_TYPE_SOURCE );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_load_jxl_source_init( VipsForeignLoadJxlSource *jxl )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*HAVE_LIBJXL*/
|
||||||
|
|
||||||
|
/* The C API wrappers are defined in foreign.c.
|
||||||
|
*/
|
||||||
|
@ -36,132 +36,658 @@
|
|||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
#include <vips/intl.h>
|
#include <vips/intl.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBJXL
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
#include <vips/internal.h>
|
||||||
|
|
||||||
/**
|
#include <jxl/encode.h>
|
||||||
* vips_jxlsave: (method)
|
#include <jxl/thread_parallel_runner.h>
|
||||||
* @in: image to save
|
|
||||||
* @filename: file to write to
|
#include "pforeign.h"
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
|
/* TODO:
|
||||||
*
|
*
|
||||||
* Optional arguments:
|
* - libjxl currently seems to be missing API to attach a profile
|
||||||
*
|
*
|
||||||
* * @tier: %gint, decode speed tier
|
* - libjxl encode only works in one shot mode, so there's no way to write in
|
||||||
* * @distance: %gdouble, maximum encoding error
|
* chunks
|
||||||
* * @effort: %gint, encoding effort
|
|
||||||
* * @lossless: %gboolean, enables lossless compression
|
|
||||||
* * @Q: %gint, quality setting
|
|
||||||
*
|
*
|
||||||
* Write a VIPS image to a file in JPEG-XL format.
|
* - add metadata support EXIF, XMP, etc. api for this is on the way
|
||||||
*
|
*
|
||||||
* The JPEG-XL loader and saver are experimental features and may change
|
* - add animation support
|
||||||
* in future libvips versions.
|
|
||||||
*
|
*
|
||||||
* @tier sets the overall decode speed the encoder will target. Minimum is 0
|
* - libjxl is currently missing error messages (I think)
|
||||||
* (highest quality), and maximum is 4 (lowest quality). Default is 0.
|
|
||||||
*
|
*
|
||||||
* @distance sets the target maximum encoding error. Minimum is 0
|
* - fix scRGB gamma
|
||||||
* (highest quality), and maximum is 15 (lowest quality). Default is 1.0
|
|
||||||
* (visually lossless).
|
|
||||||
*
|
|
||||||
* As a convenience, you can also use @Q to set @distance. @Q uses
|
|
||||||
* approximately the same scale as regular JPEG.
|
|
||||||
*
|
|
||||||
* Set @lossless to enable lossless compresion.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
*/
|
||||||
int
|
|
||||||
vips_jxlsave( VipsImage *in, const char *filename, ... )
|
#define OUTPUT_BUFFER_SIZE (4096)
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveJxl {
|
||||||
|
VipsForeignSave parent_object;
|
||||||
|
|
||||||
|
/* Where to write (set by subclasses).
|
||||||
|
*/
|
||||||
|
VipsTarget *target;
|
||||||
|
|
||||||
|
/* Encoder options.
|
||||||
|
*/
|
||||||
|
int tier;
|
||||||
|
double distance;
|
||||||
|
int effort;
|
||||||
|
gboolean lossless;
|
||||||
|
int Q;
|
||||||
|
|
||||||
|
/* Base image properties.
|
||||||
|
*/
|
||||||
|
JxlBasicInfo info;
|
||||||
|
JxlColorEncoding color_encoding;
|
||||||
|
JxlPixelFormat format;
|
||||||
|
|
||||||
|
/* Encoder state.
|
||||||
|
*/
|
||||||
|
void *runner;
|
||||||
|
JxlEncoder *encoder;
|
||||||
|
|
||||||
|
/* Write buffer.
|
||||||
|
*/
|
||||||
|
uint8_t output_buffer[OUTPUT_BUFFER_SIZE];
|
||||||
|
|
||||||
|
} VipsForeignSaveJxl;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveClass VipsForeignSaveJxlClass;
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveJxl, vips_foreign_save_jxl,
|
||||||
|
VIPS_TYPE_FOREIGN_SAVE );
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_dispose( GObject *gobject )
|
||||||
{
|
{
|
||||||
va_list ap;
|
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) gobject;
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, filename );
|
VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
|
||||||
result = vips_call_split( "jxlsave", ap, in, filename );
|
VIPS_FREEF( JxlEncoderDestroy, jxl->encoder );
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
return( result );
|
G_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
|
||||||
|
dispose( gobject );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* vips_jxlsave_buffer: (method)
|
vips_foreign_save_jxl_error( VipsForeignSaveJxl *jxl, const char *details )
|
||||||
* @in: image to save
|
|
||||||
* @buf: (array length=len) (element-type guint8): return output buffer here
|
|
||||||
* @len: (type gsize): return output length here
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Optional arguments:
|
|
||||||
*
|
|
||||||
* * @tier: %gint, decode speed tier
|
|
||||||
* * @distance: %gdouble, maximum encoding error
|
|
||||||
* * @effort: %gint, encoding effort
|
|
||||||
* * @lossless: %gboolean, enables lossless compression
|
|
||||||
* * @Q: %gint, quality setting
|
|
||||||
*
|
|
||||||
* As vips_jxlsave(), but save to a memory buffer.
|
|
||||||
*
|
|
||||||
* See also: vips_jxlsave(), vips_image_write_to_target().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_jxlsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
||||||
VipsArea *area;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
area = NULL;
|
/* TODO ... jxl has no way to get error messages at the moment.
|
||||||
|
*/
|
||||||
|
vips_error( class->nickname, "error %s", details );
|
||||||
|
}
|
||||||
|
|
||||||
va_start( ap, len );
|
#ifdef DEBUG
|
||||||
result = vips_call_split( "jxlsave_buffer", ap, in, &area );
|
static void
|
||||||
va_end( ap );
|
vips_foreign_save_jxl_print_info( JxlBasicInfo *info )
|
||||||
|
{
|
||||||
|
printf( "JxlBasicInfo:\n" );
|
||||||
|
printf( " have_container = %d\n", info->have_container );
|
||||||
|
printf( " xsize = %d\n", info->xsize );
|
||||||
|
printf( " ysize = %d\n", info->ysize );
|
||||||
|
printf( " bits_per_sample = %d\n", info->bits_per_sample );
|
||||||
|
printf( " exponent_bits_per_sample = %d\n",
|
||||||
|
info->exponent_bits_per_sample );
|
||||||
|
printf( " intensity_target = %g\n", info->intensity_target );
|
||||||
|
printf( " min_nits = %g\n", info->min_nits );
|
||||||
|
printf( " relative_to_max_display = %d\n",
|
||||||
|
info->relative_to_max_display );
|
||||||
|
printf( " linear_below = %g\n", info->linear_below );
|
||||||
|
printf( " uses_original_profile = %d\n",
|
||||||
|
info->uses_original_profile );
|
||||||
|
printf( " have_preview = %d\n", info->have_preview );
|
||||||
|
printf( " have_animation = %d\n", info->have_animation );
|
||||||
|
printf( " orientation = %d\n", info->orientation );
|
||||||
|
printf( " num_color_channels = %d\n", info->num_color_channels );
|
||||||
|
printf( " num_extra_channels = %d\n", info->num_extra_channels );
|
||||||
|
printf( " alpha_bits = %d\n", info->alpha_bits );
|
||||||
|
printf( " alpha_exponent_bits = %d\n", info->alpha_exponent_bits );
|
||||||
|
printf( " alpha_premultiplied = %d\n", info->alpha_premultiplied );
|
||||||
|
printf( " preview.xsize = %d\n", info->preview.xsize );
|
||||||
|
printf( " preview.ysize = %d\n", info->preview.ysize );
|
||||||
|
printf( " animation.tps_numerator = %d\n",
|
||||||
|
info->animation.tps_numerator );
|
||||||
|
printf( " animation.tps_denominator = %d\n",
|
||||||
|
info->animation.tps_denominator );
|
||||||
|
printf( " animation.num_loops = %d\n", info->animation.num_loops );
|
||||||
|
printf( " animation.have_timecodes = %d\n",
|
||||||
|
info->animation.have_timecodes );
|
||||||
|
}
|
||||||
|
|
||||||
if( !result &&
|
static void
|
||||||
area ) {
|
vips_foreign_save_jxl_print_format( JxlPixelFormat *format )
|
||||||
if( buf ) {
|
{
|
||||||
*buf = area->data;
|
printf( "JxlPixelFormat:\n" );
|
||||||
area->free_fn = NULL;
|
printf( " data_type = " );
|
||||||
|
switch( format->data_type ) {
|
||||||
|
case JXL_TYPE_UINT8:
|
||||||
|
printf( "JXL_TYPE_UINT8" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT16:
|
||||||
|
printf( "JXL_TYPE_UINT16" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_UINT32:
|
||||||
|
printf( "JXL_TYPE_UINT32" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_TYPE_FLOAT:
|
||||||
|
printf( "JXL_TYPE_FLOAT" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf( "(unknown)" );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if( len )
|
printf( "\n" );
|
||||||
*len = area->length;
|
printf( " num_channels = %d\n", format->num_channels );
|
||||||
|
printf( " endianness = %d\n", format->endianness );
|
||||||
|
printf( " align = %zd\n", format->align );
|
||||||
|
}
|
||||||
|
|
||||||
vips_area_unref( area );
|
static void
|
||||||
|
vips_foreign_save_jxl_print_status( JxlEncoderStatus status )
|
||||||
|
{
|
||||||
|
switch( status ) {
|
||||||
|
case JXL_ENC_SUCCESS:
|
||||||
|
printf( "JXL_ENC_SUCCESS\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_ENC_ERROR:
|
||||||
|
printf( "JXL_ENC_ERROR\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_ENC_NEED_MORE_OUTPUT:
|
||||||
|
printf( "JXL_ENC_NEED_MORE_OUTPUT\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JXL_ENC_NOT_SUPPORTED:
|
||||||
|
printf( "JXL_ENC_NOT_SUPPORTED\n" );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf( "JXL_ENC_<unknown>\n" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_jxl_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||||
|
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
||||||
|
|
||||||
|
JxlEncoderOptions *options;
|
||||||
|
JxlEncoderStatus status;
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* If Q is set and distance is not, use Q to set a rough distance
|
||||||
|
* value. Formula stolen from cjxl.c and very roughly approximates
|
||||||
|
* libjpeg values.
|
||||||
|
*/
|
||||||
|
if( !vips_object_argument_isset( object, "distance" ) )
|
||||||
|
jxl->distance = jxl->Q >= 30 ?
|
||||||
|
0.1 + (100 - jxl->Q) * 0.09 :
|
||||||
|
6.4 + pow(2.5, (30 - jxl->Q) / 5.0f) / 6.25f;
|
||||||
|
|
||||||
|
/* Distance 0 is lossless. libjxl will fail for lossy distance 0.
|
||||||
|
*/
|
||||||
|
if( jxl->distance == 0 )
|
||||||
|
jxl->lossless = TRUE;
|
||||||
|
|
||||||
|
jxl->runner = JxlThreadParallelRunnerCreate( NULL,
|
||||||
|
vips_concurrency_get() );
|
||||||
|
jxl->encoder = JxlEncoderCreate( NULL );
|
||||||
|
|
||||||
|
if( JxlEncoderSetParallelRunner( jxl->encoder,
|
||||||
|
JxlThreadParallelRunner, jxl->runner ) ) {
|
||||||
|
vips_foreign_save_jxl_error( jxl,
|
||||||
|
"JxlDecoderSetParallelRunner" );
|
||||||
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( result );
|
switch( save->ready->BandFmt ) {
|
||||||
}
|
case VIPS_FORMAT_UCHAR:
|
||||||
|
jxl->info.bits_per_sample = 8;
|
||||||
|
jxl->info.exponent_bits_per_sample = 0;
|
||||||
|
jxl->format.data_type = JXL_TYPE_UINT8;
|
||||||
|
break;
|
||||||
|
|
||||||
/**
|
case VIPS_FORMAT_USHORT:
|
||||||
* vips_jxlsave_target: (method)
|
jxl->info.bits_per_sample = 16;
|
||||||
* @in: image to save
|
jxl->info.exponent_bits_per_sample = 0;
|
||||||
* @target: save image to this target
|
jxl->format.data_type = JXL_TYPE_UINT16;
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_UINT:
|
||||||
|
jxl->info.bits_per_sample = 32;
|
||||||
|
jxl->info.exponent_bits_per_sample = 0;
|
||||||
|
jxl->format.data_type = JXL_TYPE_UINT32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_FORMAT_FLOAT:
|
||||||
|
jxl->info.bits_per_sample = 32;
|
||||||
|
jxl->info.exponent_bits_per_sample = 8;
|
||||||
|
jxl->format.data_type = JXL_TYPE_FLOAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( save->ready->Type ) {
|
||||||
|
case VIPS_INTERPRETATION_B_W:
|
||||||
|
case VIPS_INTERPRETATION_GREY16:
|
||||||
|
jxl->info.num_color_channels = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_INTERPRETATION_sRGB:
|
||||||
|
case VIPS_INTERPRETATION_scRGB:
|
||||||
|
case VIPS_INTERPRETATION_RGB16:
|
||||||
|
jxl->info.num_color_channels = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
jxl->info.num_color_channels = save->ready->Bands;
|
||||||
|
}
|
||||||
|
jxl->info.num_extra_channels = VIPS_MAX( 0,
|
||||||
|
save->ready->Bands - jxl->info.num_color_channels );
|
||||||
|
|
||||||
|
jxl->info.xsize = save->ready->Xsize;
|
||||||
|
jxl->info.ysize = save->ready->Ysize;
|
||||||
|
jxl->format.num_channels = save->ready->Bands;
|
||||||
|
jxl->format.endianness = JXL_NATIVE_ENDIAN;
|
||||||
|
jxl->format.align = 0;
|
||||||
|
|
||||||
|
if( vips_image_hasalpha( save->ready ) ) {
|
||||||
|
jxl->info.alpha_bits = jxl->info.bits_per_sample;
|
||||||
|
jxl->info.alpha_exponent_bits =
|
||||||
|
jxl->info.exponent_bits_per_sample;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jxl->info.alpha_exponent_bits = 0;
|
||||||
|
jxl->info.alpha_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( vips_image_get_typeof( save->ready, "stonits" ) ) {
|
||||||
|
double stonits;
|
||||||
|
|
||||||
|
if( vips_image_get_double( save->ready, "stonits", &stonits ) )
|
||||||
|
return( -1 );
|
||||||
|
jxl->info.intensity_target = stonits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME libjxl doesn't seem to have this API yet.
|
||||||
*
|
*
|
||||||
* Optional arguments:
|
if( vips_image_get_typeof( save->ready, VIPS_META_ICC_NAME ) ) {
|
||||||
*
|
const void *data;
|
||||||
* * @tier: %gint, decode speed tier
|
size_t length;
|
||||||
* * @distance: %gdouble, maximum encoding error
|
|
||||||
* * @effort: %gint, encoding effort
|
if( vips_image_get_blob( save->ready,
|
||||||
* * @lossless: %gboolean, enables lossless compression
|
VIPS_META_ICC_NAME, &data, &length ) )
|
||||||
* * @Q: %gint, quality setting
|
return( -1 );
|
||||||
*
|
|
||||||
* As vips_jxlsave(), but save to a target.
|
jxl->info.uses_original_profile = JXL_TRUE;
|
||||||
*
|
... attach profile
|
||||||
* See also: vips_jxlsave(), vips_image_write_to_target().
|
}
|
||||||
*
|
else
|
||||||
* Returns: 0 on success, -1 on error.
|
jxl->info.uses_original_profile = JXL_FALSE;
|
||||||
*/
|
*/
|
||||||
int
|
|
||||||
vips_jxlsave_target( VipsImage *in, VipsTarget *target, ... )
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, target );
|
/* Remove this when libjxl gets API to attach an ICC profile.
|
||||||
result = vips_call_split( "jxlsave_target", ap, in, target );
|
*/
|
||||||
va_end( ap );
|
jxl->info.uses_original_profile = JXL_FALSE;
|
||||||
|
|
||||||
return( result );
|
if( JxlEncoderSetBasicInfo( jxl->encoder, &jxl->info ) ) {
|
||||||
|
vips_foreign_save_jxl_error( jxl, "JxlEncoderSetBasicInfo" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlColorEncodingSetToSRGB( &jxl->color_encoding,
|
||||||
|
jxl->format.num_channels < 3 );
|
||||||
|
if( JxlEncoderSetColorEncoding( jxl->encoder, &jxl->color_encoding ) ) {
|
||||||
|
vips_foreign_save_jxl_error( jxl,
|
||||||
|
"JxlEncoderSetColorEncoding" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Render the entire image in memory. libjxl seems to be missing
|
||||||
|
* tile-based write at the moment.
|
||||||
|
*/
|
||||||
|
if( vips_image_wio_input( save->ready ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
options = JxlEncoderOptionsCreate( jxl->encoder, NULL );
|
||||||
|
JxlEncoderOptionsSetDecodingSpeed( options, jxl->tier );
|
||||||
|
JxlEncoderOptionsSetDistance( options, jxl->distance );
|
||||||
|
JxlEncoderOptionsSetEffort( options, jxl->effort );
|
||||||
|
JxlEncoderOptionsSetLossless( options, jxl->lossless );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_foreign_save_jxl_print_info( &jxl->info );
|
||||||
|
vips_foreign_save_jxl_print_format( &jxl->format );
|
||||||
|
printf( "JxlEncoderOptions:\n" );
|
||||||
|
printf( " tier = %d\n", jxl->tier );
|
||||||
|
printf( " distance = %g\n", jxl->distance );
|
||||||
|
printf( " effort = %d\n", jxl->effort );
|
||||||
|
printf( " lossless = %d\n", jxl->lossless );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
if( JxlEncoderAddImageFrame( options, &jxl->format,
|
||||||
|
VIPS_IMAGE_ADDR( save->ready, 0, 0 ),
|
||||||
|
VIPS_IMAGE_SIZEOF_IMAGE( save->ready ) ) ) {
|
||||||
|
vips_foreign_save_jxl_error( jxl, "JxlEncoderAddImageFrame" );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
uint8_t *out;
|
||||||
|
size_t avail_out;
|
||||||
|
|
||||||
|
out = jxl->output_buffer;
|
||||||
|
avail_out = OUTPUT_BUFFER_SIZE;
|
||||||
|
status = JxlEncoderProcessOutput( jxl->encoder,
|
||||||
|
&out, &avail_out );
|
||||||
|
switch( status ) {
|
||||||
|
case JXL_ENC_SUCCESS:
|
||||||
|
case JXL_ENC_NEED_MORE_OUTPUT:
|
||||||
|
if( vips_target_write( jxl->target,
|
||||||
|
jxl->output_buffer,
|
||||||
|
OUTPUT_BUFFER_SIZE - avail_out ) )
|
||||||
|
return( -1 );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
vips_foreign_save_jxl_error( jxl,
|
||||||
|
"JxlEncoderProcessOutput" );
|
||||||
|
#ifdef DEBUG
|
||||||
|
vips_foreign_save_jxl_print_status( status );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
} while( status != JXL_ENC_SUCCESS );
|
||||||
|
|
||||||
|
vips_target_finish( jxl->target );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save a bit of typing.
|
||||||
|
*/
|
||||||
|
#define UC VIPS_FORMAT_UCHAR
|
||||||
|
#define US VIPS_FORMAT_USHORT
|
||||||
|
#define UI VIPS_FORMAT_UINT
|
||||||
|
#define F VIPS_FORMAT_FLOAT
|
||||||
|
|
||||||
|
/* Type promotion for save ... unsigned ints + float + double.
|
||||||
|
*/
|
||||||
|
static int bandfmt_jpeg[10] = {
|
||||||
|
/* UC C US S UI I F X D DX */
|
||||||
|
UC, UC, US, US, UI, UI, F, F, F, F
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_class_init( VipsForeignSaveJxlClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||||
|
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||||
|
|
||||||
|
gobject_class->dispose = vips_foreign_save_jxl_dispose;
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlsave_base";
|
||||||
|
object_class->description = _( "save image in JPEG-XL format" );
|
||||||
|
object_class->build = vips_foreign_save_jxl_build;
|
||||||
|
|
||||||
|
foreign_class->suffs = vips__jxl_suffs;
|
||||||
|
|
||||||
|
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||||
|
save_class->format_table = bandfmt_jpeg;
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "tier", 10,
|
||||||
|
_( "Tier" ),
|
||||||
|
_( "Decode speed tier" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxl, tier ),
|
||||||
|
0, 4, 0 );
|
||||||
|
|
||||||
|
VIPS_ARG_DOUBLE( class, "distance", 11,
|
||||||
|
_( "Distance" ),
|
||||||
|
_( "Target butteraugli distance" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxl, distance ),
|
||||||
|
0, 15, 1.0 );
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "effort", 12,
|
||||||
|
_( "effort" ),
|
||||||
|
_( "Encoding effort" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxl, effort ),
|
||||||
|
3, 9, 7 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOOL( class, "lossless", 13,
|
||||||
|
_( "Lossless" ),
|
||||||
|
_( "Enable lossless compression" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxl, lossless ),
|
||||||
|
FALSE );
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "Q", 14,
|
||||||
|
_( "Q" ),
|
||||||
|
_( "Quality factor" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxl, Q ),
|
||||||
|
0, 100, 75 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_init( VipsForeignSaveJxl *jxl )
|
||||||
|
{
|
||||||
|
jxl->tier = 0;
|
||||||
|
jxl->distance = 1.0;
|
||||||
|
jxl->effort = 7;
|
||||||
|
jxl->lossless = FALSE;
|
||||||
|
jxl->Q = 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveJxlFile {
|
||||||
|
VipsForeignSaveJxl parent_object;
|
||||||
|
|
||||||
|
/* Filename for save.
|
||||||
|
*/
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
} VipsForeignSaveJxlFile;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlFileClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveJxlFile, vips_foreign_save_jxl_file,
|
||||||
|
vips_foreign_save_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_jxl_file_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
||||||
|
VipsForeignSaveJxlFile *file = (VipsForeignSaveJxlFile *) object;
|
||||||
|
|
||||||
|
if( !(jxl->target = vips_target_new_to_file( file->filename )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_file_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_file_class_init( VipsForeignSaveJxlFileClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlsave";
|
||||||
|
object_class->build = vips_foreign_save_jxl_file_build;
|
||||||
|
|
||||||
|
VIPS_ARG_STRING( class, "filename", 1,
|
||||||
|
_( "Filename" ),
|
||||||
|
_( "Filename to load from" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxlFile, filename ),
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_file_init( VipsForeignSaveJxlFile *file )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveJxlBuffer {
|
||||||
|
VipsForeignSaveJxl parent_object;
|
||||||
|
|
||||||
|
/* Save to a buffer.
|
||||||
|
*/
|
||||||
|
VipsArea *buf;
|
||||||
|
|
||||||
|
} VipsForeignSaveJxlBuffer;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlBufferClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveJxlBuffer, vips_foreign_save_jxl_buffer,
|
||||||
|
vips_foreign_save_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_jxl_buffer_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
||||||
|
VipsForeignSaveJxlBuffer *buffer =
|
||||||
|
(VipsForeignSaveJxlBuffer *) object;
|
||||||
|
|
||||||
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
if( !(jxl->target = vips_target_new_to_memory()) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_buffer_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
g_object_get( jxl->target, "blob", &blob, NULL );
|
||||||
|
g_object_set( buffer, "buffer", blob, NULL );
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_buffer_class_init(
|
||||||
|
VipsForeignSaveJxlBufferClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlsave_buffer";
|
||||||
|
object_class->build = vips_foreign_save_jxl_buffer_build;
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||||
|
_( "Buffer" ),
|
||||||
|
_( "Buffer to save to" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxlBuffer, buf ),
|
||||||
|
VIPS_TYPE_BLOB );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_buffer_init( VipsForeignSaveJxlBuffer *buffer )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _VipsForeignSaveJxlTarget {
|
||||||
|
VipsForeignSaveJxl parent_object;
|
||||||
|
|
||||||
|
VipsTarget *target;
|
||||||
|
} VipsForeignSaveJxlTarget;
|
||||||
|
|
||||||
|
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlTargetClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsForeignSaveJxlTarget, vips_foreign_save_jxl_target,
|
||||||
|
vips_foreign_save_jxl_get_type() );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_save_jxl_target_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
||||||
|
VipsForeignSaveJxlTarget *target =
|
||||||
|
(VipsForeignSaveJxlTarget *) object;
|
||||||
|
|
||||||
|
if( target->target ) {
|
||||||
|
jxl->target = target->target;
|
||||||
|
g_object_ref( jxl->target );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_target_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_target_class_init(
|
||||||
|
VipsForeignSaveJxlTargetClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||||
|
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
object_class->nickname = "jxlsave_target";
|
||||||
|
object_class->build = vips_foreign_save_jxl_target_build;
|
||||||
|
|
||||||
|
VIPS_ARG_OBJECT( class, "target", 1,
|
||||||
|
_( "Target" ),
|
||||||
|
_( "Target to save to" ),
|
||||||
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignSaveJxlTarget, target ),
|
||||||
|
VIPS_TYPE_TARGET );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_foreign_save_jxl_target_init( VipsForeignSaveJxlTarget *target )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*HAVE_LIBJXL*/
|
||||||
|
|
||||||
|
/* The C API wrappers are defined in foreign.c.
|
||||||
|
*/
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -747,6 +747,33 @@ vips_foreign_load_pdf_file_build( VipsObject *object )
|
|||||||
build( object ) );
|
build( object ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
|
||||||
|
{
|
||||||
|
const guchar *str = (const guchar *) buf;
|
||||||
|
|
||||||
|
if( len >= 4 &&
|
||||||
|
str[0] == '%' &&
|
||||||
|
str[1] == 'P' &&
|
||||||
|
str[2] == 'D' &&
|
||||||
|
str[3] == 'F' )
|
||||||
|
return( 1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_pdf_is_a( const char *filename )
|
||||||
|
{
|
||||||
|
unsigned char buf[4];
|
||||||
|
|
||||||
|
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
|
||||||
|
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
|
||||||
|
return( 1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_foreign_load_pdf_file_class_init(
|
vips_foreign_load_pdf_file_class_init(
|
||||||
VipsForeignLoadPdfFileClass *class )
|
VipsForeignLoadPdfFileClass *class )
|
||||||
|
@ -1,237 +0,0 @@
|
|||||||
/* load PDF with libpoppler
|
|
||||||
*
|
|
||||||
* 7/2/16
|
|
||||||
* - from openslideload.c
|
|
||||||
* 12/5/16
|
|
||||||
* - add @n ... number of pages to load
|
|
||||||
* 23/11/16
|
|
||||||
* - set page-height, if we can
|
|
||||||
* 28/6/17
|
|
||||||
* - use a much larger strip size, thanks bubba
|
|
||||||
* 8/6/18
|
|
||||||
* - add background param
|
|
||||||
* 16/8/18 [kleisauke]
|
|
||||||
* - shut down the input file as soon as we can
|
|
||||||
* 19/9/19
|
|
||||||
* - reopen the input if we minimised too early
|
|
||||||
* 11/3/20
|
|
||||||
* - move on top of VipsSource
|
|
||||||
* 21/9/20
|
|
||||||
* - allow dpi and scale to both be set [le0daniel]
|
|
||||||
* 21/4/21 kleisauke
|
|
||||||
* - move GObject part to poppler2vips.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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define DEBUG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
#include <vips/intl.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <vips/vips.h>
|
|
||||||
#include <vips/buf.h>
|
|
||||||
#include <vips/internal.h>
|
|
||||||
|
|
||||||
#include "pforeign.h"
|
|
||||||
|
|
||||||
/* Also used by the pdfium loader.
|
|
||||||
*/
|
|
||||||
gboolean
|
|
||||||
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
|
|
||||||
{
|
|
||||||
const guchar *str = (const guchar *) buf;
|
|
||||||
|
|
||||||
if( len >= 4 &&
|
|
||||||
str[0] == '%' &&
|
|
||||||
str[1] == 'P' &&
|
|
||||||
str[2] == 'D' &&
|
|
||||||
str[3] == 'F' )
|
|
||||||
return( 1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Also used by the pdfium loader.
|
|
||||||
*/
|
|
||||||
gboolean
|
|
||||||
vips_foreign_load_pdf_is_a( const char *filename )
|
|
||||||
{
|
|
||||||
unsigned char buf[4];
|
|
||||||
|
|
||||||
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
|
|
||||||
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
|
|
||||||
return( 1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vips_pdfload:
|
|
||||||
* @filename: file to load
|
|
||||||
* @out: (out): output image
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Optional arguments:
|
|
||||||
*
|
|
||||||
* * @page: %gint, load this page, numbered from zero
|
|
||||||
* * @n: %gint, load this many pages
|
|
||||||
* * @dpi: %gdouble, render at this DPI
|
|
||||||
* * @scale: %gdouble, scale render by this factor
|
|
||||||
* * @background: #VipsArrayDouble background colour
|
|
||||||
*
|
|
||||||
* Render a PDF file into a VIPS image. Rendering uses the libpoppler library
|
|
||||||
* and should be fast.
|
|
||||||
*
|
|
||||||
* The output image is always RGBA --- CMYK PDFs will be
|
|
||||||
* converted. If you need CMYK bitmaps, you should use vips_magickload()
|
|
||||||
* instead.
|
|
||||||
*
|
|
||||||
* Use @page to select a page to render, numbering from zero.
|
|
||||||
*
|
|
||||||
* Use @n to select the number of pages to render. The default is 1. Pages are
|
|
||||||
* rendered in a vertical column, with each individual page aligned to the
|
|
||||||
* left. Set to -1 to mean "until the end of the document". Use vips_grid()
|
|
||||||
* to change page layout.
|
|
||||||
*
|
|
||||||
* Use @dpi to set the rendering resolution. The default is 72. Additionally,
|
|
||||||
* you can scale by setting @scale. If you set both, they combine.
|
|
||||||
*
|
|
||||||
* Use @background to set the background RGBA colour. The default is 255
|
|
||||||
* (solid white), use eg. 0 for a transparent background.
|
|
||||||
*
|
|
||||||
* The operation fills a number of header fields with metadata, for example
|
|
||||||
* "pdf-author". They may be useful.
|
|
||||||
*
|
|
||||||
* This function only reads the image header and does not render any pixel
|
|
||||||
* data. Rendering occurs when pixels are accessed.
|
|
||||||
*
|
|
||||||
* See also: vips_image_new_from_file(), vips_magickload().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_pdfload( const char *filename, VipsImage **out, ... )
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, out );
|
|
||||||
result = vips_call_split( "pdfload", ap, filename, out );
|
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vips_pdfload_buffer:
|
|
||||||
* @buf: (array length=len) (element-type guint8): memory area to load
|
|
||||||
* @len: (type gsize): size of memory area
|
|
||||||
* @out: (out): image to write
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Optional arguments:
|
|
||||||
*
|
|
||||||
* * @page: %gint, load this page, numbered from zero
|
|
||||||
* * @n: %gint, load this many pages
|
|
||||||
* * @dpi: %gdouble, render at this DPI
|
|
||||||
* * @scale: %gdouble, scale render by this factor
|
|
||||||
* * @background: #VipsArrayDouble background colour
|
|
||||||
*
|
|
||||||
* Read a PDF-formatted memory buffer into a VIPS image. Exactly as
|
|
||||||
* vips_pdfload(), but read from memory.
|
|
||||||
*
|
|
||||||
* You must not free the buffer while @out is active. The
|
|
||||||
* #VipsObject::postclose signal on @out is a good place to free.
|
|
||||||
*
|
|
||||||
* See also: vips_pdfload().
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_pdfload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
VipsBlob *blob;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
/* We don't take a copy of the data or free it.
|
|
||||||
*/
|
|
||||||
blob = vips_blob_new( NULL, buf, len );
|
|
||||||
|
|
||||||
va_start( ap, out );
|
|
||||||
result = vips_call_split( "pdfload_buffer", ap, blob, out );
|
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
vips_area_unref( VIPS_AREA( blob ) );
|
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vips_pdfload_source:
|
|
||||||
* @source: source to load from
|
|
||||||
* @out: (out): image to write
|
|
||||||
* @...: %NULL-terminated list of optional named arguments
|
|
||||||
*
|
|
||||||
* Optional arguments:
|
|
||||||
*
|
|
||||||
* * @page: %gint, load this page, numbered from zero
|
|
||||||
* * @n: %gint, load this many pages
|
|
||||||
* * @dpi: %gdouble, render at this DPI
|
|
||||||
* * @scale: %gdouble, scale render by this factor
|
|
||||||
* * @background: #VipsArrayDouble background colour
|
|
||||||
*
|
|
||||||
* Exactly as vips_pdfload(), but read from a source.
|
|
||||||
*
|
|
||||||
* See also: vips_pdfload()
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
vips_pdfload_source( VipsSource *source, VipsImage **out, ... )
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start( ap, out );
|
|
||||||
result = vips_call_split( "pdfload_source", ap, source, out );
|
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
return( result );
|
|
||||||
}
|
|
||||||
|
|
@ -215,9 +215,6 @@ int vips__webp_write_target( VipsImage *image, VipsTarget *target,
|
|||||||
gboolean min_size, int kmin, int kmax,
|
gboolean min_size, int kmin, int kmax,
|
||||||
gboolean strip, const char *profile );
|
gboolean strip, const char *profile );
|
||||||
|
|
||||||
gboolean vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len );
|
|
||||||
gboolean vips_foreign_load_pdf_is_a( const char *filename );
|
|
||||||
|
|
||||||
int vips__quantise_image( VipsImage *in,
|
int vips__quantise_image( VipsImage *in,
|
||||||
VipsImage **index_out, VipsImage **palette_out,
|
VipsImage **index_out, VipsImage **palette_out,
|
||||||
int colours, int Q, double dither );
|
int colours, int Q, double dither );
|
||||||
|
@ -641,6 +641,33 @@ vips_foreign_load_pdf_file_build( VipsObject *object )
|
|||||||
build( object ) );
|
build( object ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
|
||||||
|
{
|
||||||
|
const guchar *str = (const guchar *) buf;
|
||||||
|
|
||||||
|
if( len >= 4 &&
|
||||||
|
str[0] == '%' &&
|
||||||
|
str[1] == 'P' &&
|
||||||
|
str[2] == 'D' &&
|
||||||
|
str[3] == 'F' )
|
||||||
|
return( 1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_pdf_is_a( const char *filename )
|
||||||
|
{
|
||||||
|
unsigned char buf[4];
|
||||||
|
|
||||||
|
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
|
||||||
|
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
|
||||||
|
return( 1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_foreign_load_pdf_file_class_init(
|
vips_foreign_load_pdf_file_class_init(
|
||||||
VipsForeignLoadPdfFileClass *class )
|
VipsForeignLoadPdfFileClass *class )
|
||||||
@ -809,3 +836,6 @@ vips_foreign_load_pdf_source_init( VipsForeignLoadPdfSource *source )
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif /*defined(HAVE_POPPLER)*/
|
#endif /*defined(HAVE_POPPLER)*/
|
||||||
|
|
||||||
|
/* The C API wrappers are defined in foreign.c.
|
||||||
|
*/
|
@ -1,719 +0,0 @@
|
|||||||
/* save to heif
|
|
||||||
*
|
|
||||||
* 5/7/18
|
|
||||||
* - from niftisave.c
|
|
||||||
* 3/7/19 [lovell]
|
|
||||||
* - add "compression" option
|
|
||||||
* 1/9/19 [meyermarcel]
|
|
||||||
* - save alpha when necessary
|
|
||||||
* 15/3/20
|
|
||||||
* - revise for new VipsTarget API
|
|
||||||
* 14/2/21 kleisauke
|
|
||||||
* - include GObject part from heifsave.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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define DEBUG_VERBOSE
|
|
||||||
#define DEBUG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
#include <vips/intl.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_HEIF_ENCODER
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <vips/vips.h>
|
|
||||||
#include <vips/internal.h>
|
|
||||||
|
|
||||||
#include "pforeign.h"
|
|
||||||
|
|
||||||
#include <libheif/heif.h>
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveHeif {
|
|
||||||
VipsForeignSave parent_object;
|
|
||||||
|
|
||||||
/* Where to write (set by subclasses).
|
|
||||||
*/
|
|
||||||
VipsTarget *target;
|
|
||||||
|
|
||||||
/* Coding quality factor (1-100).
|
|
||||||
*/
|
|
||||||
int Q;
|
|
||||||
|
|
||||||
/* Lossless compression.
|
|
||||||
*/
|
|
||||||
gboolean lossless;
|
|
||||||
|
|
||||||
/* Compression format
|
|
||||||
*/
|
|
||||||
VipsForeignHeifCompression compression;
|
|
||||||
|
|
||||||
/* CPU effort (0-8).
|
|
||||||
*/
|
|
||||||
int speed;
|
|
||||||
|
|
||||||
/* Chroma subsampling.
|
|
||||||
*/
|
|
||||||
VipsForeignSubsample subsample_mode;
|
|
||||||
|
|
||||||
/* The image we save. This is a copy of save->ready since we need to
|
|
||||||
* be able to update the metadata.
|
|
||||||
*/
|
|
||||||
VipsImage *image;
|
|
||||||
|
|
||||||
int page_width;
|
|
||||||
int page_height;
|
|
||||||
int n_pages;
|
|
||||||
|
|
||||||
struct heif_context *ctx;
|
|
||||||
struct heif_encoder *encoder;
|
|
||||||
|
|
||||||
/* The current page we are writing.
|
|
||||||
*/
|
|
||||||
struct heif_image_handle *handle;
|
|
||||||
|
|
||||||
/* The current page in memory which we build as we scan down the
|
|
||||||
* image.
|
|
||||||
*/
|
|
||||||
struct heif_image *img;
|
|
||||||
|
|
||||||
/* The libheif memory area we fill with pixels from the libvips
|
|
||||||
* pipe.
|
|
||||||
*/
|
|
||||||
uint8_t *data;
|
|
||||||
int stride;
|
|
||||||
|
|
||||||
} VipsForeignSaveHeif;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveClass VipsForeignSaveHeifClass;
|
|
||||||
|
|
||||||
/* Defined in heif2vips.c
|
|
||||||
*/
|
|
||||||
void vips__heif_error( struct heif_error *error );
|
|
||||||
void vips__heif_image_print( struct heif_image *img );
|
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveHeif, vips_foreign_save_heif,
|
|
||||||
VIPS_TYPE_FOREIGN_SAVE );
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_dispose( GObject *gobject )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) gobject;
|
|
||||||
|
|
||||||
VIPS_UNREF( heif->target );
|
|
||||||
VIPS_UNREF( heif->image );
|
|
||||||
VIPS_FREEF( heif_image_release, heif->img );
|
|
||||||
VIPS_FREEF( heif_image_handle_release, heif->handle );
|
|
||||||
VIPS_FREEF( heif_encoder_release, heif->encoder );
|
|
||||||
VIPS_FREEF( heif_context_free, heif->ctx );
|
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
|
|
||||||
dispose( gobject );
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
|
|
||||||
const struct heif_image_handle *,
|
|
||||||
const void *, int );
|
|
||||||
|
|
||||||
struct _VipsForeignSaveHeifMetadata {
|
|
||||||
const char *name;
|
|
||||||
libheif_metadata_fn saver;
|
|
||||||
} libheif_metadata[] = {
|
|
||||||
{ VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
|
|
||||||
{ VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct heif_error error;
|
|
||||||
|
|
||||||
/* Rebuild exif from tags, if we'll be saving it.
|
|
||||||
*/
|
|
||||||
if( vips_image_get_typeof( heif->image, VIPS_META_EXIF_NAME ) )
|
|
||||||
if( vips__exif_update( heif->image ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
for( i = 0; i < VIPS_NUMBER( libheif_metadata ); i++ )
|
|
||||||
if( vips_image_get_typeof( heif->image,
|
|
||||||
libheif_metadata[i].name ) ) {
|
|
||||||
const void *data;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "attaching %s ..\n",
|
|
||||||
libheif_metadata[i].name );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
if( vips_image_get_blob( heif->image,
|
|
||||||
libheif_metadata[i].name, &data, &length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
error = libheif_metadata[i].saver( heif->ctx,
|
|
||||||
heif->handle, data, length );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
|
|
||||||
{
|
|
||||||
VipsForeignSave *save = (VipsForeignSave *) heif;
|
|
||||||
|
|
||||||
struct heif_error error;
|
|
||||||
struct heif_encoding_options *options;
|
|
||||||
|
|
||||||
#ifdef HAVE_HEIF_COLOR_PROFILE
|
|
||||||
if( !save->strip &&
|
|
||||||
vips_image_get_typeof( heif->image, VIPS_META_ICC_NAME ) ) {
|
|
||||||
const void *data;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "attaching profile ..\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
if( vips_image_get_blob( heif->image,
|
|
||||||
VIPS_META_ICC_NAME, &data, &length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* FIXME .. also see heif_image_set_nclx_color_profile()
|
|
||||||
*/
|
|
||||||
error = heif_image_set_raw_color_profile( heif->img,
|
|
||||||
"rICC", data, length );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /*HAVE_HEIF_COLOR_PROFILE*/
|
|
||||||
|
|
||||||
options = heif_encoding_options_alloc();
|
|
||||||
if( vips_image_hasalpha( heif->image ) )
|
|
||||||
options->save_alpha_channel = 1;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "encoding ..\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
error = heif_context_encode_image( heif->ctx,
|
|
||||||
heif->img, heif->encoder, options, &heif->handle );
|
|
||||||
|
|
||||||
heif_encoding_options_free( options );
|
|
||||||
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
|
|
||||||
int primary;
|
|
||||||
|
|
||||||
if( vips_image_get_int( heif->image,
|
|
||||||
"heif-primary", &primary ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( page == primary ) {
|
|
||||||
error = heif_context_set_primary_image( heif->ctx,
|
|
||||||
heif->handle );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !save->strip &&
|
|
||||||
vips_foreign_save_heif_write_metadata( heif ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
VIPS_FREEF( heif_image_handle_release, heif->handle );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
|
|
||||||
void *a )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) a;
|
|
||||||
|
|
||||||
int y;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_save_heif_write_block: y = %d\n", area->top );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
/* Copy a line at a time into our output image, write each time the
|
|
||||||
* image fills.
|
|
||||||
*/
|
|
||||||
for( y = 0; y < area->height; y++ ) {
|
|
||||||
/* Y in page.
|
|
||||||
*/
|
|
||||||
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 ) );
|
|
||||||
|
|
||||||
/* Did we just write the final line? Write as a new page
|
|
||||||
* into the output.
|
|
||||||
*/
|
|
||||||
if( line == heif->page_height - 1 )
|
|
||||||
if( vips_foreign_save_heif_write_page( heif, page ) )
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
struct heif_error
|
|
||||||
vips_foreign_save_heif_write( struct heif_context *ctx,
|
|
||||||
const void *data, size_t length, void *userdata )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
|
|
||||||
|
|
||||||
struct heif_error error;
|
|
||||||
|
|
||||||
error.code = 0;
|
|
||||||
if( vips_target_write( heif->target, data, length ) )
|
|
||||||
error.code = -1;
|
|
||||||
|
|
||||||
return( error );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
|
||||||
|
|
||||||
const char *filename;
|
|
||||||
struct heif_error error;
|
|
||||||
struct heif_writer writer;
|
|
||||||
char *chroma;
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Make a copy of the image in case we modify the metadata eg. for
|
|
||||||
* exif_update.
|
|
||||||
*/
|
|
||||||
if( vips_copy( save->ready, &heif->image, NULL ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Compression defaults to VIPS_FOREIGN_HEIF_COMPRESSION_AV1 for .avif
|
|
||||||
* suffix.
|
|
||||||
*/
|
|
||||||
filename = vips_connection_filename( VIPS_CONNECTION( heif->target ) );
|
|
||||||
if( !vips_object_argument_isset( object, "compression" ) &&
|
|
||||||
filename &&
|
|
||||||
vips_iscasepostfix( filename, ".avif" ) )
|
|
||||||
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
|
||||||
|
|
||||||
error = heif_context_get_encoder_for_format( heif->ctx,
|
|
||||||
(enum heif_compression_format) heif->compression,
|
|
||||||
&heif->encoder );
|
|
||||||
if( error.code ) {
|
|
||||||
if( error.code == heif_error_Unsupported_filetype )
|
|
||||||
vips_error( "heifsave",
|
|
||||||
"%s", _( "Unsupported compression" ) );
|
|
||||||
else
|
|
||||||
vips__heif_error( &error );
|
|
||||||
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
error = heif_encoder_set_lossy_quality( heif->encoder, heif->Q );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
error = heif_encoder_set_lossless( heif->encoder, heif->lossless );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
error = heif_encoder_set_parameter_integer( heif->encoder,
|
|
||||||
"speed", heif->speed );
|
|
||||||
if( error.code &&
|
|
||||||
error.subcode != heif_suberror_Unsupported_parameter ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
|
|
||||||
( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
|
|
||||||
heif->Q >= 90 ) ? "444" : "420";
|
|
||||||
error = heif_encoder_set_parameter_string( heif->encoder,
|
|
||||||
"chroma", chroma );
|
|
||||||
if( error.code &&
|
|
||||||
error.subcode != heif_suberror_Unsupported_parameter ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO .. support extra per-encoder params with
|
|
||||||
* heif_encoder_list_parameters().
|
|
||||||
*/
|
|
||||||
|
|
||||||
heif->page_width = heif->image->Xsize;
|
|
||||||
heif->page_height = vips_image_get_page_height( heif->image );
|
|
||||||
heif->n_pages = heif->image->Ysize / heif->page_height;
|
|
||||||
|
|
||||||
/* Make a heif image the size of a page. We send sink_disc() output
|
|
||||||
* here and write a frame each time it fills.
|
|
||||||
*/
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "vips_foreign_save_heif_build:\n" );
|
|
||||||
printf( "\twidth = %d\n", heif->page_width );
|
|
||||||
printf( "\theight = %d\n", heif->page_height );
|
|
||||||
printf( "\talpha = %d\n", vips_image_hasalpha( heif->image ) );
|
|
||||||
#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,
|
|
||||||
&heif->img );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
|
|
||||||
heif->page_width, heif->page_height,
|
|
||||||
vips_image_hasalpha( heif->image ) ? 32 : 24 );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
vips__heif_image_print( heif->img );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
heif->data = heif_image_get_plane( heif->img,
|
|
||||||
heif_channel_interleaved, &heif->stride );
|
|
||||||
|
|
||||||
/* Write data.
|
|
||||||
*/
|
|
||||||
if( vips_sink_disc( heif->image,
|
|
||||||
vips_foreign_save_heif_write_block, heif ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* This has to come right at the end :-( so there's no support for
|
|
||||||
* incremental writes.
|
|
||||||
*/
|
|
||||||
writer.writer_api_version = 1;
|
|
||||||
writer.write = vips_foreign_save_heif_write;
|
|
||||||
error = heif_context_write( heif->ctx, &writer, heif );
|
|
||||||
if( error.code ) {
|
|
||||||
vips__heif_error( &error );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
vips_target_finish( heif->target );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save a bit of typing.
|
|
||||||
*/
|
|
||||||
#define UC VIPS_FORMAT_UCHAR
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
|
||||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
|
||||||
|
|
||||||
gobject_class->dispose = vips_foreign_save_heif_dispose;
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "heifsave_base";
|
|
||||||
object_class->description = _( "save image in HEIF format" );
|
|
||||||
object_class->build = vips_foreign_save_heif_build;
|
|
||||||
|
|
||||||
foreign_class->suffs = vips__heif_suffs;
|
|
||||||
|
|
||||||
save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
|
|
||||||
save_class->format_table = vips_heif_bandfmt;
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "Q", 10,
|
|
||||||
_( "Q" ),
|
|
||||||
_( "Q factor" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
|
|
||||||
1, 100, 50 );
|
|
||||||
|
|
||||||
VIPS_ARG_BOOL( class, "lossless", 13,
|
|
||||||
_( "Lossless" ),
|
|
||||||
_( "Enable lossless compression" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, lossless ),
|
|
||||||
FALSE );
|
|
||||||
|
|
||||||
VIPS_ARG_ENUM( class, "compression", 14,
|
|
||||||
_( "compression" ),
|
|
||||||
_( "Compression format" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, compression ),
|
|
||||||
VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
||||||
VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "speed", 15,
|
|
||||||
_( "speed" ),
|
|
||||||
_( "CPU effort" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
|
|
||||||
0, 9, 5 );
|
|
||||||
|
|
||||||
VIPS_ARG_ENUM( class, "subsample_mode", 16,
|
|
||||||
_( "Subsample mode" ),
|
|
||||||
_( "Select chroma subsample operation mode" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
|
|
||||||
VIPS_TYPE_FOREIGN_SUBSAMPLE,
|
|
||||||
VIPS_FOREIGN_SUBSAMPLE_AUTO );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
|
|
||||||
{
|
|
||||||
heif->ctx = heif_context_alloc();
|
|
||||||
heif->Q = 50;
|
|
||||||
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
|
|
||||||
heif->speed = 5;
|
|
||||||
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveHeifFile {
|
|
||||||
VipsForeignSaveHeif parent_object;
|
|
||||||
|
|
||||||
/* Filename for save.
|
|
||||||
*/
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
} VipsForeignSaveHeifFile;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifFileClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveHeifFile, vips_foreign_save_heif_file,
|
|
||||||
vips_foreign_save_heif_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_file_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
|
||||||
VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
|
|
||||||
|
|
||||||
if( !(heif->target = vips_target_new_to_file( file->filename )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "heifsave";
|
|
||||||
object_class->build = vips_foreign_save_heif_file_build;
|
|
||||||
|
|
||||||
VIPS_ARG_STRING( class, "filename", 1,
|
|
||||||
_( "Filename" ),
|
|
||||||
_( "Filename to save to" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeifFile, filename ),
|
|
||||||
NULL );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_file_init( VipsForeignSaveHeifFile *file )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveHeifBuffer {
|
|
||||||
VipsForeignSaveHeif parent_object;
|
|
||||||
|
|
||||||
/* Save to a buffer.
|
|
||||||
*/
|
|
||||||
VipsArea *buf;
|
|
||||||
|
|
||||||
} VipsForeignSaveHeifBuffer;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer,
|
|
||||||
vips_foreign_save_heif_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_buffer_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
|
||||||
VipsForeignSaveHeifBuffer *buffer =
|
|
||||||
(VipsForeignSaveHeifBuffer *) object;
|
|
||||||
|
|
||||||
VipsBlob *blob;
|
|
||||||
|
|
||||||
if( !(heif->target = vips_target_new_to_memory()) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_buffer_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
g_object_get( heif->target, "blob", &blob, NULL );
|
|
||||||
g_object_set( buffer, "buffer", blob, NULL );
|
|
||||||
vips_area_unref( VIPS_AREA( blob ) );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_buffer_class_init(
|
|
||||||
VipsForeignSaveHeifBufferClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "heifsave_buffer";
|
|
||||||
object_class->build = vips_foreign_save_heif_buffer_build;
|
|
||||||
|
|
||||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
|
||||||
_( "Buffer" ),
|
|
||||||
_( "Buffer to save to" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeifBuffer, buf ),
|
|
||||||
VIPS_TYPE_BLOB );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_buffer_init( VipsForeignSaveHeifBuffer *buffer )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveHeifTarget {
|
|
||||||
VipsForeignSaveHeif parent_object;
|
|
||||||
|
|
||||||
VipsTarget *target;
|
|
||||||
} VipsForeignSaveHeifTarget;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifTargetClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveHeifTarget, vips_foreign_save_heif_target,
|
|
||||||
vips_foreign_save_heif_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_heif_target_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
|
|
||||||
VipsForeignSaveHeifTarget *target =
|
|
||||||
(VipsForeignSaveHeifTarget *) object;
|
|
||||||
|
|
||||||
if( target->target ) {
|
|
||||||
heif->target = target->target;
|
|
||||||
g_object_ref( heif->target );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_target_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_target_class_init(
|
|
||||||
VipsForeignSaveHeifTargetClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "heifsave_target";
|
|
||||||
object_class->build = vips_foreign_save_heif_target_build;
|
|
||||||
|
|
||||||
VIPS_ARG_OBJECT( class, "target", 1,
|
|
||||||
_( "Target" ),
|
|
||||||
_( "Target to save to" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveHeifTarget, target ),
|
|
||||||
VIPS_TYPE_TARGET );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /*HAVE_HEIF_ENCODER*/
|
|
@ -1,695 +0,0 @@
|
|||||||
/* save as jpeg-xl
|
|
||||||
*
|
|
||||||
* 18/3/20
|
|
||||||
* - from heifload.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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define DEBUG_VERBOSE
|
|
||||||
#define DEBUG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
#include <vips/intl.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <vips/vips.h>
|
|
||||||
#include <vips/internal.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBJXL
|
|
||||||
|
|
||||||
#include <jxl/encode.h>
|
|
||||||
#include <jxl/thread_parallel_runner.h>
|
|
||||||
|
|
||||||
#include "pforeign.h"
|
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
*
|
|
||||||
* - libjxl currently seems to be missing API to attach a profile
|
|
||||||
*
|
|
||||||
* - libjxl encode only works in one shot mode, so there's no way to write in
|
|
||||||
* chunks
|
|
||||||
*
|
|
||||||
* - add metadata support EXIF, XMP, etc. api for this is on the way
|
|
||||||
*
|
|
||||||
* - add animation support
|
|
||||||
*
|
|
||||||
* - libjxl is currently missing error messages (I think)
|
|
||||||
*
|
|
||||||
* - fix scRGB gamma
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define OUTPUT_BUFFER_SIZE (4096)
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveJxl {
|
|
||||||
VipsForeignSave parent_object;
|
|
||||||
|
|
||||||
/* Where to write (set by subclasses).
|
|
||||||
*/
|
|
||||||
VipsTarget *target;
|
|
||||||
|
|
||||||
/* Encoder options.
|
|
||||||
*/
|
|
||||||
int tier;
|
|
||||||
double distance;
|
|
||||||
int effort;
|
|
||||||
gboolean lossless;
|
|
||||||
int Q;
|
|
||||||
|
|
||||||
/* Base image properties.
|
|
||||||
*/
|
|
||||||
JxlBasicInfo info;
|
|
||||||
JxlColorEncoding color_encoding;
|
|
||||||
JxlPixelFormat format;
|
|
||||||
|
|
||||||
/* Encoder state.
|
|
||||||
*/
|
|
||||||
void *runner;
|
|
||||||
JxlEncoder *encoder;
|
|
||||||
|
|
||||||
/* Write buffer.
|
|
||||||
*/
|
|
||||||
uint8_t output_buffer[OUTPUT_BUFFER_SIZE];
|
|
||||||
|
|
||||||
} VipsForeignSaveJxl;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveClass VipsForeignSaveJxlClass;
|
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveJxl, vips_foreign_save_jxl,
|
|
||||||
VIPS_TYPE_FOREIGN_SAVE );
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_dispose( GObject *gobject )
|
|
||||||
{
|
|
||||||
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) gobject;
|
|
||||||
|
|
||||||
VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
|
|
||||||
VIPS_FREEF( JxlEncoderDestroy, jxl->encoder );
|
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
|
|
||||||
dispose( gobject );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_error( VipsForeignSaveJxl *jxl, const char *details )
|
|
||||||
{
|
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
|
|
||||||
|
|
||||||
/* TODO ... jxl has no way to get error messages at the moment.
|
|
||||||
*/
|
|
||||||
vips_error( class->nickname, "error %s", details );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_print_info( JxlBasicInfo *info )
|
|
||||||
{
|
|
||||||
printf( "JxlBasicInfo:\n" );
|
|
||||||
printf( " have_container = %d\n", info->have_container );
|
|
||||||
printf( " xsize = %d\n", info->xsize );
|
|
||||||
printf( " ysize = %d\n", info->ysize );
|
|
||||||
printf( " bits_per_sample = %d\n", info->bits_per_sample );
|
|
||||||
printf( " exponent_bits_per_sample = %d\n",
|
|
||||||
info->exponent_bits_per_sample );
|
|
||||||
printf( " intensity_target = %g\n", info->intensity_target );
|
|
||||||
printf( " min_nits = %g\n", info->min_nits );
|
|
||||||
printf( " relative_to_max_display = %d\n",
|
|
||||||
info->relative_to_max_display );
|
|
||||||
printf( " linear_below = %g\n", info->linear_below );
|
|
||||||
printf( " uses_original_profile = %d\n",
|
|
||||||
info->uses_original_profile );
|
|
||||||
printf( " have_preview = %d\n", info->have_preview );
|
|
||||||
printf( " have_animation = %d\n", info->have_animation );
|
|
||||||
printf( " orientation = %d\n", info->orientation );
|
|
||||||
printf( " num_color_channels = %d\n", info->num_color_channels );
|
|
||||||
printf( " num_extra_channels = %d\n", info->num_extra_channels );
|
|
||||||
printf( " alpha_bits = %d\n", info->alpha_bits );
|
|
||||||
printf( " alpha_exponent_bits = %d\n", info->alpha_exponent_bits );
|
|
||||||
printf( " alpha_premultiplied = %d\n", info->alpha_premultiplied );
|
|
||||||
printf( " preview.xsize = %d\n", info->preview.xsize );
|
|
||||||
printf( " preview.ysize = %d\n", info->preview.ysize );
|
|
||||||
printf( " animation.tps_numerator = %d\n",
|
|
||||||
info->animation.tps_numerator );
|
|
||||||
printf( " animation.tps_denominator = %d\n",
|
|
||||||
info->animation.tps_denominator );
|
|
||||||
printf( " animation.num_loops = %d\n", info->animation.num_loops );
|
|
||||||
printf( " animation.have_timecodes = %d\n",
|
|
||||||
info->animation.have_timecodes );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_print_format( JxlPixelFormat *format )
|
|
||||||
{
|
|
||||||
printf( "JxlPixelFormat:\n" );
|
|
||||||
printf( " data_type = " );
|
|
||||||
switch( format->data_type ) {
|
|
||||||
case JXL_TYPE_UINT8:
|
|
||||||
printf( "JXL_TYPE_UINT8" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT16:
|
|
||||||
printf( "JXL_TYPE_UINT16" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
printf( "JXL_TYPE_UINT32" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_TYPE_FLOAT:
|
|
||||||
printf( "JXL_TYPE_FLOAT" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf( "(unknown)" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf( "\n" );
|
|
||||||
printf( " num_channels = %d\n", format->num_channels );
|
|
||||||
printf( " endianness = %d\n", format->endianness );
|
|
||||||
printf( " align = %zd\n", format->align );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_print_status( JxlEncoderStatus status )
|
|
||||||
{
|
|
||||||
switch( status ) {
|
|
||||||
case JXL_ENC_SUCCESS:
|
|
||||||
printf( "JXL_ENC_SUCCESS\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_ENC_ERROR:
|
|
||||||
printf( "JXL_ENC_ERROR\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_ENC_NEED_MORE_OUTPUT:
|
|
||||||
printf( "JXL_ENC_NEED_MORE_OUTPUT\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JXL_ENC_NOT_SUPPORTED:
|
|
||||||
printf( "JXL_ENC_NOT_SUPPORTED\n" );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf( "JXL_ENC_<unknown>\n" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_jxl_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
|
||||||
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
|
||||||
|
|
||||||
JxlEncoderOptions *options;
|
|
||||||
JxlEncoderStatus status;
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* If Q is set and distance is not, use Q to set a rough distance
|
|
||||||
* value. Formula stolen from cjxl.c and very roughly approximates
|
|
||||||
* libjpeg values.
|
|
||||||
*/
|
|
||||||
if( !vips_object_argument_isset( object, "distance" ) )
|
|
||||||
jxl->distance = jxl->Q >= 30 ?
|
|
||||||
0.1 + (100 - jxl->Q) * 0.09 :
|
|
||||||
6.4 + pow(2.5, (30 - jxl->Q) / 5.0f) / 6.25f;
|
|
||||||
|
|
||||||
/* Distance 0 is lossless. libjxl will fail for lossy distance 0.
|
|
||||||
*/
|
|
||||||
if( jxl->distance == 0 )
|
|
||||||
jxl->lossless = TRUE;
|
|
||||||
|
|
||||||
jxl->runner = JxlThreadParallelRunnerCreate( NULL,
|
|
||||||
vips_concurrency_get() );
|
|
||||||
jxl->encoder = JxlEncoderCreate( NULL );
|
|
||||||
|
|
||||||
if( JxlEncoderSetParallelRunner( jxl->encoder,
|
|
||||||
JxlThreadParallelRunner, jxl->runner ) ) {
|
|
||||||
vips_foreign_save_jxl_error( jxl,
|
|
||||||
"JxlDecoderSetParallelRunner" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( save->ready->BandFmt ) {
|
|
||||||
case VIPS_FORMAT_UCHAR:
|
|
||||||
jxl->info.bits_per_sample = 8;
|
|
||||||
jxl->info.exponent_bits_per_sample = 0;
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
|
||||||
jxl->info.bits_per_sample = 16;
|
|
||||||
jxl->info.exponent_bits_per_sample = 0;
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_UINT:
|
|
||||||
jxl->info.bits_per_sample = 32;
|
|
||||||
jxl->info.exponent_bits_per_sample = 0;
|
|
||||||
jxl->format.data_type = JXL_TYPE_UINT32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
|
||||||
jxl->info.bits_per_sample = 32;
|
|
||||||
jxl->info.exponent_bits_per_sample = 8;
|
|
||||||
jxl->format.data_type = JXL_TYPE_FLOAT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
g_assert_not_reached();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( save->ready->Type ) {
|
|
||||||
case VIPS_INTERPRETATION_B_W:
|
|
||||||
case VIPS_INTERPRETATION_GREY16:
|
|
||||||
jxl->info.num_color_channels = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIPS_INTERPRETATION_sRGB:
|
|
||||||
case VIPS_INTERPRETATION_scRGB:
|
|
||||||
case VIPS_INTERPRETATION_RGB16:
|
|
||||||
jxl->info.num_color_channels = 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
jxl->info.num_color_channels = save->ready->Bands;
|
|
||||||
}
|
|
||||||
jxl->info.num_extra_channels = VIPS_MAX( 0,
|
|
||||||
save->ready->Bands - jxl->info.num_color_channels );
|
|
||||||
|
|
||||||
jxl->info.xsize = save->ready->Xsize;
|
|
||||||
jxl->info.ysize = save->ready->Ysize;
|
|
||||||
jxl->format.num_channels = save->ready->Bands;
|
|
||||||
jxl->format.endianness = JXL_NATIVE_ENDIAN;
|
|
||||||
jxl->format.align = 0;
|
|
||||||
|
|
||||||
if( vips_image_hasalpha( save->ready ) ) {
|
|
||||||
jxl->info.alpha_bits = jxl->info.bits_per_sample;
|
|
||||||
jxl->info.alpha_exponent_bits =
|
|
||||||
jxl->info.exponent_bits_per_sample;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jxl->info.alpha_exponent_bits = 0;
|
|
||||||
jxl->info.alpha_bits = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( vips_image_get_typeof( save->ready, "stonits" ) ) {
|
|
||||||
double stonits;
|
|
||||||
|
|
||||||
if( vips_image_get_double( save->ready, "stonits", &stonits ) )
|
|
||||||
return( -1 );
|
|
||||||
jxl->info.intensity_target = stonits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME libjxl doesn't seem to have this API yet.
|
|
||||||
*
|
|
||||||
if( vips_image_get_typeof( save->ready, VIPS_META_ICC_NAME ) ) {
|
|
||||||
const void *data;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
if( vips_image_get_blob( save->ready,
|
|
||||||
VIPS_META_ICC_NAME, &data, &length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
jxl->info.uses_original_profile = JXL_TRUE;
|
|
||||||
... attach profile
|
|
||||||
}
|
|
||||||
else
|
|
||||||
jxl->info.uses_original_profile = JXL_FALSE;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Remove this when libjxl gets API to attach an ICC profile.
|
|
||||||
*/
|
|
||||||
jxl->info.uses_original_profile = JXL_FALSE;
|
|
||||||
|
|
||||||
if( JxlEncoderSetBasicInfo( jxl->encoder, &jxl->info ) ) {
|
|
||||||
vips_foreign_save_jxl_error( jxl, "JxlEncoderSetBasicInfo" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
JxlColorEncodingSetToSRGB( &jxl->color_encoding,
|
|
||||||
jxl->format.num_channels < 3 );
|
|
||||||
if( JxlEncoderSetColorEncoding( jxl->encoder, &jxl->color_encoding ) ) {
|
|
||||||
vips_foreign_save_jxl_error( jxl,
|
|
||||||
"JxlEncoderSetColorEncoding" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Render the entire image in memory. libjxl seems to be missing
|
|
||||||
* tile-based write at the moment.
|
|
||||||
*/
|
|
||||||
if( vips_image_wio_input( save->ready ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
options = JxlEncoderOptionsCreate( jxl->encoder, NULL );
|
|
||||||
JxlEncoderOptionsSetDecodingSpeed( options, jxl->tier );
|
|
||||||
JxlEncoderOptionsSetDistance( options, jxl->distance );
|
|
||||||
JxlEncoderOptionsSetEffort( options, jxl->effort );
|
|
||||||
JxlEncoderOptionsSetLossless( options, jxl->lossless );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
vips_foreign_save_jxl_print_info( &jxl->info );
|
|
||||||
vips_foreign_save_jxl_print_format( &jxl->format );
|
|
||||||
printf( "JxlEncoderOptions:\n" );
|
|
||||||
printf( " tier = %d\n", jxl->tier );
|
|
||||||
printf( " distance = %g\n", jxl->distance );
|
|
||||||
printf( " effort = %d\n", jxl->effort );
|
|
||||||
printf( " lossless = %d\n", jxl->lossless );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
if( JxlEncoderAddImageFrame( options, &jxl->format,
|
|
||||||
VIPS_IMAGE_ADDR( save->ready, 0, 0 ),
|
|
||||||
VIPS_IMAGE_SIZEOF_IMAGE( save->ready ) ) ) {
|
|
||||||
vips_foreign_save_jxl_error( jxl, "JxlEncoderAddImageFrame" );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
uint8_t *out;
|
|
||||||
size_t avail_out;
|
|
||||||
|
|
||||||
out = jxl->output_buffer;
|
|
||||||
avail_out = OUTPUT_BUFFER_SIZE;
|
|
||||||
status = JxlEncoderProcessOutput( jxl->encoder,
|
|
||||||
&out, &avail_out );
|
|
||||||
switch( status ) {
|
|
||||||
case JXL_ENC_SUCCESS:
|
|
||||||
case JXL_ENC_NEED_MORE_OUTPUT:
|
|
||||||
if( vips_target_write( jxl->target,
|
|
||||||
jxl->output_buffer,
|
|
||||||
OUTPUT_BUFFER_SIZE - avail_out ) )
|
|
||||||
return( -1 );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
vips_foreign_save_jxl_error( jxl,
|
|
||||||
"JxlEncoderProcessOutput" );
|
|
||||||
#ifdef DEBUG
|
|
||||||
vips_foreign_save_jxl_print_status( status );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
} while( status != JXL_ENC_SUCCESS );
|
|
||||||
|
|
||||||
vips_target_finish( jxl->target );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save a bit of typing.
|
|
||||||
*/
|
|
||||||
#define UC VIPS_FORMAT_UCHAR
|
|
||||||
#define US VIPS_FORMAT_USHORT
|
|
||||||
#define UI VIPS_FORMAT_UINT
|
|
||||||
#define F VIPS_FORMAT_FLOAT
|
|
||||||
|
|
||||||
/* Type promotion for save ... unsigned ints + float + double.
|
|
||||||
*/
|
|
||||||
static int bandfmt_jpeg[10] = {
|
|
||||||
/* UC C US S UI I F X D DX */
|
|
||||||
UC, UC, US, US, UI, UI, F, F, F, F
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_class_init( VipsForeignSaveJxlClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
|
||||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
|
||||||
|
|
||||||
gobject_class->dispose = vips_foreign_save_jxl_dispose;
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlsave_base";
|
|
||||||
object_class->description = _( "save image in JPEG-XL format" );
|
|
||||||
object_class->build = vips_foreign_save_jxl_build;
|
|
||||||
|
|
||||||
foreign_class->suffs = vips__jxl_suffs;
|
|
||||||
|
|
||||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
|
||||||
save_class->format_table = bandfmt_jpeg;
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "tier", 10,
|
|
||||||
_( "Tier" ),
|
|
||||||
_( "Decode speed tier" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxl, tier ),
|
|
||||||
0, 4, 0 );
|
|
||||||
|
|
||||||
VIPS_ARG_DOUBLE( class, "distance", 11,
|
|
||||||
_( "Distance" ),
|
|
||||||
_( "Target butteraugli distance" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxl, distance ),
|
|
||||||
0, 15, 1.0 );
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "effort", 12,
|
|
||||||
_( "effort" ),
|
|
||||||
_( "Encoding effort" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxl, effort ),
|
|
||||||
3, 9, 7 );
|
|
||||||
|
|
||||||
VIPS_ARG_BOOL( class, "lossless", 13,
|
|
||||||
_( "Lossless" ),
|
|
||||||
_( "Enable lossless compression" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxl, lossless ),
|
|
||||||
FALSE );
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "Q", 14,
|
|
||||||
_( "Q" ),
|
|
||||||
_( "Quality factor" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxl, Q ),
|
|
||||||
0, 100, 75 );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_init( VipsForeignSaveJxl *jxl )
|
|
||||||
{
|
|
||||||
jxl->tier = 0;
|
|
||||||
jxl->distance = 1.0;
|
|
||||||
jxl->effort = 7;
|
|
||||||
jxl->lossless = FALSE;
|
|
||||||
jxl->Q = 75;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveJxlFile {
|
|
||||||
VipsForeignSaveJxl parent_object;
|
|
||||||
|
|
||||||
/* Filename for save.
|
|
||||||
*/
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
} VipsForeignSaveJxlFile;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlFileClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveJxlFile, vips_foreign_save_jxl_file,
|
|
||||||
vips_foreign_save_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_jxl_file_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
|
||||||
VipsForeignSaveJxlFile *file = (VipsForeignSaveJxlFile *) object;
|
|
||||||
|
|
||||||
if( !(jxl->target = vips_target_new_to_file( file->filename )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_file_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_file_class_init( VipsForeignSaveJxlFileClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlsave";
|
|
||||||
object_class->build = vips_foreign_save_jxl_file_build;
|
|
||||||
|
|
||||||
VIPS_ARG_STRING( class, "filename", 1,
|
|
||||||
_( "Filename" ),
|
|
||||||
_( "Filename to load from" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxlFile, filename ),
|
|
||||||
NULL );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_file_init( VipsForeignSaveJxlFile *file )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveJxlBuffer {
|
|
||||||
VipsForeignSaveJxl parent_object;
|
|
||||||
|
|
||||||
/* Save to a buffer.
|
|
||||||
*/
|
|
||||||
VipsArea *buf;
|
|
||||||
|
|
||||||
} VipsForeignSaveJxlBuffer;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlBufferClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveJxlBuffer, vips_foreign_save_jxl_buffer,
|
|
||||||
vips_foreign_save_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_jxl_buffer_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
|
||||||
VipsForeignSaveJxlBuffer *buffer =
|
|
||||||
(VipsForeignSaveJxlBuffer *) object;
|
|
||||||
|
|
||||||
VipsBlob *blob;
|
|
||||||
|
|
||||||
if( !(jxl->target = vips_target_new_to_memory()) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_buffer_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
g_object_get( jxl->target, "blob", &blob, NULL );
|
|
||||||
g_object_set( buffer, "buffer", blob, NULL );
|
|
||||||
vips_area_unref( VIPS_AREA( blob ) );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_buffer_class_init(
|
|
||||||
VipsForeignSaveJxlBufferClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlsave_buffer";
|
|
||||||
object_class->build = vips_foreign_save_jxl_buffer_build;
|
|
||||||
|
|
||||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
|
||||||
_( "Buffer" ),
|
|
||||||
_( "Buffer to save to" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxlBuffer, buf ),
|
|
||||||
VIPS_TYPE_BLOB );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_buffer_init( VipsForeignSaveJxlBuffer *buffer )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsForeignSaveJxlTarget {
|
|
||||||
VipsForeignSaveJxl parent_object;
|
|
||||||
|
|
||||||
VipsTarget *target;
|
|
||||||
} VipsForeignSaveJxlTarget;
|
|
||||||
|
|
||||||
typedef VipsForeignSaveJxlClass VipsForeignSaveJxlTargetClass;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsForeignSaveJxlTarget, vips_foreign_save_jxl_target,
|
|
||||||
vips_foreign_save_jxl_get_type() );
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_save_jxl_target_build( VipsObject *object )
|
|
||||||
{
|
|
||||||
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
|
|
||||||
VipsForeignSaveJxlTarget *target =
|
|
||||||
(VipsForeignSaveJxlTarget *) object;
|
|
||||||
|
|
||||||
if( target->target ) {
|
|
||||||
jxl->target = target->target;
|
|
||||||
g_object_ref( jxl->target );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_target_parent_class )->
|
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_target_class_init(
|
|
||||||
VipsForeignSaveJxlTargetClass *class )
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
||||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
||||||
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
|
||||||
gobject_class->get_property = vips_object_get_property;
|
|
||||||
|
|
||||||
object_class->nickname = "jxlsave_target";
|
|
||||||
object_class->build = vips_foreign_save_jxl_target_build;
|
|
||||||
|
|
||||||
VIPS_ARG_OBJECT( class, "target", 1,
|
|
||||||
_( "Target" ),
|
|
||||||
_( "Target to save to" ),
|
|
||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignSaveJxlTarget, target ),
|
|
||||||
VIPS_TYPE_TARGET );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_save_jxl_target_init( VipsForeignSaveJxlTarget *target )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /*HAVE_LIBJXL*/
|
|
Loading…
x
Reference in New Issue
Block a user