From 3b57e1345230cd400b008e76fb1d8614be2cb4df Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Sun, 15 Mar 2020 18:17:08 +0000
Subject: [PATCH] move heifsave to new target API

and add heifsave_target
---
 ChangeLog                      |   2 +-
 libvips/foreign/foreign.c      |   2 +
 libvips/foreign/heifload.c     |   2 +
 libvips/foreign/heifsave.c     | 184 ++++++++++++++++++++++++---------
 libvips/include/vips/foreign.h |   2 +
 5 files changed, 142 insertions(+), 50 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 324650d4..7c7ae490 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,7 +6,7 @@
 - tiffsave has a "depth" param to set max pyr depth
 - libtiff LOGLUV images load and save as libvips XYZ 
 - add gifload_source, csvload_source, csvsave_target, matrixload_source,
-  matrixsave_source, pdfload_source, heifload_source
+  matrixsave_source, pdfload_source, heifload_source, heifsave_target
 - revise vipsthumbnail flags
 - add VIPS_LEAK env var
 - add vips_pipe_read_limit_set(), --vips-pipe-read-limit,
diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c
index 7ba93edb..b849e9bb 100644
--- a/libvips/foreign/foreign.c
+++ b/libvips/foreign/foreign.c
@@ -2127,6 +2127,7 @@ vips_foreign_operation_init( void )
 	extern GType vips_foreign_load_heif_source_get_type( void ); 
 	extern GType vips_foreign_save_heif_file_get_type( void ); 
 	extern GType vips_foreign_save_heif_buffer_get_type( void ); 
+	extern GType vips_foreign_save_heif_target_get_type( void ); 
 
 	extern GType vips_foreign_load_nifti_get_type( void ); 
 	extern GType vips_foreign_save_nifti_get_type( void ); 
@@ -2280,6 +2281,7 @@ vips_foreign_operation_init( void )
 #ifdef HAVE_HEIF_ENCODER
 	vips_foreign_save_heif_file_get_type(); 
 	vips_foreign_save_heif_buffer_get_type(); 
+	vips_foreign_save_heif_target_get_type(); 
 #endif /*HAVE_HEIF_ENCODER*/
 
 	vips__foreign_load_operation = 
diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c
index b6d3c420..4aed54e0 100644
--- a/libvips/foreign/heifload.c
+++ b/libvips/foreign/heifload.c
@@ -14,6 +14,8 @@
  * 	  were reselecting the image for each scanline
  * 3/10/19
  * 	- restart after minimise
+ * 15/3/20
+ * 	- revise for new VipsSource API
  */
 
 /*
diff --git a/libvips/foreign/heifsave.c b/libvips/foreign/heifsave.c
index 23016fc8..459d8f1a 100644
--- a/libvips/foreign/heifsave.c
+++ b/libvips/foreign/heifsave.c
@@ -6,6 +6,8 @@
  * 	- add "compression" option
  * 1/9/19 [meyermarcel]
  * 	- save alpha when necessary
+ * 15/3/20
+ * 	- revise for new VipsTarget API
  */
 
 /*
@@ -61,6 +63,10 @@
 typedef struct _VipsForeignSaveHeif {
 	VipsForeignSave parent_object;
 
+	/* Where to write (set by subclasses).
+	 */
+	VipsTarget *target;
+
 	/* Coding quality factor (1-100).
 	 */
 	int Q;
@@ -112,6 +118,7 @@ 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 );
@@ -292,6 +299,21 @@ vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
 	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 )
 {
@@ -299,6 +321,7 @@ vips_foreign_save_heif_build( VipsObject *object )
 	VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
 
 	struct heif_error error;
+	struct heif_writer writer;
 
 	if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
 		build( object ) )
@@ -377,6 +400,19 @@ vips_foreign_save_heif_build( VipsObject *object )
 		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 );
 }
 
@@ -462,21 +498,13 @@ vips_foreign_save_heif_file_build( VipsObject *object )
 	VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
 	VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
 
-	struct heif_error error;
+	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 );
 
-	/* This has to come right at the end :-( so there's no support for
-	 * incremental writes.
-	 */
-	error = heif_context_write_to_file( heif->ctx, file->filename );
-	if( error.code ) {
-		vips__heif_error( &error );
-		return( -1 );
-	}
-
 	return( 0 );
 }
 
@@ -520,55 +548,25 @@ typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
 G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer, 
 	vips_foreign_save_heif_get_type() );
 
-struct heif_error 
-vips_foreign_save_heif_buffer_write( struct heif_context *ctx, 
-	const void *data, size_t length, void *userdata )
-{
-	VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
-
-	VipsBlob *blob;
-	struct heif_error error;
-	void *data_copy;
-
-	/* FIXME .. we have to memcpy()!
-	 */
-	data_copy = vips_malloc( NULL, length );
-	memcpy( data_copy, data, length );
-
-	blob = vips_blob_new( (VipsCallbackFn) vips_free, data_copy, length );
-	g_object_set( heif, "buffer", blob, NULL );
-	vips_area_unref( VIPS_AREA( blob ) );
-
-	error.code = 0;
-
-	return( error );
-}
-
 static int
 vips_foreign_save_heif_buffer_build( VipsObject *object )
 {
 	VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
+	VipsForeignSaveHeifBuffer *buffer = 
+		(VipsForeignSaveHeifBuffer *) object;
 
-	/* FIXME ... argh, allocating on the stack! But the example code does
-	 * this too.
-	 */
-	struct heif_writer writer;
-	struct heif_error error;
+	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 );
 
-	/* 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_buffer_write;
-	error = heif_context_write( heif->ctx, &writer, heif );
-	if( error.code ) {
-		vips__heif_error( &error );
-		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 );
 }
@@ -600,6 +598,63 @@ 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*/
 
 /**
@@ -690,3 +745,34 @@ vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
 
 	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
+ *
+ * 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 );
+}
diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h
index 352db415..b86aacdb 100644
--- a/libvips/include/vips/foreign.h
+++ b/libvips/include/vips/foreign.h
@@ -635,6 +635,8 @@ int vips_heifsave( VipsImage *in, const char *filename, ... )
 	__attribute__((sentinel));
 int vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
 	__attribute__((sentinel));
+int vips_heifsave_target( VipsImage *in, VipsTarget *target, ... )
+	__attribute__((sentinel));
 
 int vips_niftiload( const char *filename, VipsImage **out, ... )
 	__attribute__((sentinel));