1 /* save as jpeg-xl
2  *
3  * 18/3/20
4  * 	- from heifload.c
5  */
6 
7 /*
8 
9     This file is part of VIPS.
10 
11     VIPS is free software; you can redistribute it and/or modify
12     it under the terms of the GNU Lesser General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU Lesser General Public License for more details.
20 
21     You should have received a copy of the GNU Lesser General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24     02110-1301  USA
25 
26  */
27 
28 /*
29 
30     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31 
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif /*HAVE_CONFIG_H*/
37 #include <vips/intl.h>
38 
39 #ifdef HAVE_LIBJXL
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include <vips/vips.h>
46 #include <vips/internal.h>
47 
48 #include <jxl/encode.h>
49 #include <jxl/thread_parallel_runner.h>
50 
51 #include "pforeign.h"
52 
53 /* TODO:
54  *
55  * - libjxl currently seems to be missing API to attach a profile
56  *
57  * - libjxl encode only works in one shot mode, so there's no way to write in
58  *   chunks
59  *
60  * - add metadata support EXIF, XMP, etc. api for this is on the way
61  *
62  * - add animation support
63  *
64  * - libjxl is currently missing error messages (I think)
65  *
66  * - fix scRGB gamma
67  */
68 
69 #define OUTPUT_BUFFER_SIZE (4096)
70 
71 typedef struct _VipsForeignSaveJxl {
72 	VipsForeignSave parent_object;
73 
74 	/* Where to write (set by subclasses).
75 	 */
76 	VipsTarget *target;
77 
78 	/* Encoder options.
79 	 */
80 	int tier;
81 	double distance;
82 	int effort;
83 	gboolean lossless;
84 	int Q;
85 
86 	/* Base image properties.
87 	 */
88 	JxlBasicInfo info;
89 	JxlColorEncoding color_encoding;
90 	JxlPixelFormat format;
91 
92 	/* Encoder state.
93 	 */
94 	void *runner;
95 	JxlEncoder *encoder;
96 
97 	/* Write buffer.
98 	 */
99 	uint8_t output_buffer[OUTPUT_BUFFER_SIZE];
100 
101 } VipsForeignSaveJxl;
102 
103 typedef VipsForeignSaveClass VipsForeignSaveJxlClass;
104 
105 G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveJxl, vips_foreign_save_jxl,
106 	VIPS_TYPE_FOREIGN_SAVE );
107 
108 static void
vips_foreign_save_jxl_dispose(GObject * gobject)109 vips_foreign_save_jxl_dispose( GObject *gobject )
110 {
111 	VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) gobject;
112 
113 	VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
114 	VIPS_FREEF( JxlEncoderDestroy, jxl->encoder );
115 
116 	G_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
117 		dispose( gobject );
118 }
119 
120 static void
vips_foreign_save_jxl_error(VipsForeignSaveJxl * jxl,const char * details)121 vips_foreign_save_jxl_error( VipsForeignSaveJxl *jxl, const char *details )
122 {
123 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jxl );
124 
125 	/* TODO ... jxl has no way to get error messages at the moment.
126 	 */
127 	vips_error( class->nickname, "error %s", details );
128 }
129 
130 #ifdef DEBUG
131 static void
vips_foreign_save_jxl_print_info(JxlBasicInfo * info)132 vips_foreign_save_jxl_print_info( JxlBasicInfo *info )
133 {
134 	printf( "JxlBasicInfo:\n" );
135 	printf( "    have_container = %d\n", info->have_container );
136 	printf( "    xsize = %d\n", info->xsize );
137 	printf( "    ysize = %d\n", info->ysize );
138 	printf( "    bits_per_sample = %d\n", info->bits_per_sample );
139 	printf( "    exponent_bits_per_sample = %d\n",
140 		info->exponent_bits_per_sample );
141 	printf( "    intensity_target = %g\n", info->intensity_target );
142 	printf( "    min_nits = %g\n", info->min_nits );
143 	printf( "    relative_to_max_display = %d\n",
144 		info->relative_to_max_display );
145 	printf( "    linear_below = %g\n", info->linear_below );
146 	printf( "    uses_original_profile = %d\n",
147 		info->uses_original_profile );
148 	printf( "    have_preview = %d\n", info->have_preview );
149 	printf( "    have_animation = %d\n", info->have_animation );
150 	printf( "    orientation = %d\n", info->orientation );
151 	printf( "    num_color_channels = %d\n", info->num_color_channels );
152 	printf( "    num_extra_channels = %d\n", info->num_extra_channels );
153 	printf( "    alpha_bits = %d\n", info->alpha_bits );
154 	printf( "    alpha_exponent_bits = %d\n", info->alpha_exponent_bits );
155 	printf( "    alpha_premultiplied = %d\n", info->alpha_premultiplied );
156 	printf( "    preview.xsize = %d\n", info->preview.xsize );
157 	printf( "    preview.ysize = %d\n", info->preview.ysize );
158 	printf( "    animation.tps_numerator = %d\n",
159 		info->animation.tps_numerator );
160 	printf( "    animation.tps_denominator = %d\n",
161 		info->animation.tps_denominator );
162 	printf( "    animation.num_loops = %d\n", info->animation.num_loops );
163 	printf( "    animation.have_timecodes = %d\n",
164 		info->animation.have_timecodes );
165 }
166 
167 static void
vips_foreign_save_jxl_print_format(JxlPixelFormat * format)168 vips_foreign_save_jxl_print_format( JxlPixelFormat *format )
169 {
170 	printf( "JxlPixelFormat:\n" );
171 	printf( "    data_type = " );
172 	switch( format->data_type ) {
173 	case JXL_TYPE_UINT8:
174 		printf( "JXL_TYPE_UINT8" );
175 		break;
176 
177 	case JXL_TYPE_UINT16:
178 		printf( "JXL_TYPE_UINT16" );
179 		break;
180 
181 	case JXL_TYPE_UINT32:
182 		printf( "JXL_TYPE_UINT32" );
183 		break;
184 
185 	case JXL_TYPE_FLOAT:
186 		printf( "JXL_TYPE_FLOAT" );
187 		break;
188 
189 	default:
190 		printf( "(unknown)" );
191 		break;
192 	}
193 	printf( "\n" );
194 	printf( "    num_channels = %d\n", format->num_channels );
195 	printf( "    endianness = %d\n", format->endianness );
196 	printf( "    align = %zd\n", format->align );
197 }
198 
199 static void
vips_foreign_save_jxl_print_status(JxlEncoderStatus status)200 vips_foreign_save_jxl_print_status( JxlEncoderStatus status )
201 {
202 	switch( status ) {
203 	case JXL_ENC_SUCCESS:
204 		printf( "JXL_ENC_SUCCESS\n" );
205 		break;
206 
207 	case JXL_ENC_ERROR:
208 		printf( "JXL_ENC_ERROR\n" );
209 		break;
210 
211 	case JXL_ENC_NEED_MORE_OUTPUT:
212 		printf( "JXL_ENC_NEED_MORE_OUTPUT\n" );
213 		break;
214 
215 	case JXL_ENC_NOT_SUPPORTED:
216 		printf( "JXL_ENC_NOT_SUPPORTED\n" );
217 		break;
218 
219 	default:
220 		printf( "JXL_ENC_<unknown>\n" );
221 		break;
222 	}
223 }
224 #endif /*DEBUG*/
225 
226 static int
vips_foreign_save_jxl_build(VipsObject * object)227 vips_foreign_save_jxl_build( VipsObject *object )
228 {
229 	VipsForeignSave *save = (VipsForeignSave *) object;
230 	VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
231 
232 	JxlEncoderOptions *options;
233 	JxlEncoderStatus status;
234 
235 	if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
236 		build( object ) )
237 		return( -1 );
238 
239 	/* If Q is set and distance is not, use Q to set a rough distance
240 	 * value. Formula stolen from cjxl.c and very roughly approximates
241 	 * libjpeg values.
242 	 */
243 	if( !vips_object_argument_isset( object, "distance" ) )
244 		jxl->distance = jxl->Q >= 30 ?
245 			0.1 + (100 - jxl->Q) * 0.09 :
246 			6.4 + pow(2.5, (30 - jxl->Q) / 5.0f) / 6.25f;
247 
248 	/* Distance 0 is lossless. libjxl will fail for lossy distance 0.
249 	 */
250 	if( jxl->distance == 0 )
251 		jxl->lossless = TRUE;
252 
253 	jxl->runner = JxlThreadParallelRunnerCreate( NULL,
254 		vips_concurrency_get() );
255 	jxl->encoder = JxlEncoderCreate( NULL );
256 
257 	if( JxlEncoderSetParallelRunner( jxl->encoder,
258 		JxlThreadParallelRunner, jxl->runner ) ) {
259 		vips_foreign_save_jxl_error( jxl,
260 			"JxlDecoderSetParallelRunner" );
261 		return( -1 );
262 	}
263 
264 #ifdef HAVE_LIBJXL_JXLENCODERINITBASICINFO
265 	JxlEncoderInitBasicInfo( &jxl->info );
266 #endif
267 
268 	switch( save->ready->BandFmt ) {
269 	case VIPS_FORMAT_UCHAR:
270 		jxl->info.bits_per_sample = 8;
271 		jxl->info.exponent_bits_per_sample = 0;
272 		jxl->format.data_type = JXL_TYPE_UINT8;
273 		break;
274 
275 	case VIPS_FORMAT_USHORT:
276 		jxl->info.bits_per_sample = 16;
277 		jxl->info.exponent_bits_per_sample = 0;
278 		jxl->format.data_type = JXL_TYPE_UINT16;
279 		break;
280 
281 	case VIPS_FORMAT_UINT:
282 		jxl->info.bits_per_sample = 32;
283 		jxl->info.exponent_bits_per_sample = 0;
284 		jxl->format.data_type = JXL_TYPE_UINT32;
285 		break;
286 
287 	case VIPS_FORMAT_FLOAT:
288 		jxl->info.bits_per_sample = 32;
289 		jxl->info.exponent_bits_per_sample = 8;
290 		jxl->format.data_type = JXL_TYPE_FLOAT;
291 		break;
292 
293 	default:
294 		g_assert_not_reached();
295 		break;
296 	}
297 
298 	switch( save->ready->Type ) {
299 	case VIPS_INTERPRETATION_B_W:
300 	case VIPS_INTERPRETATION_GREY16:
301 		jxl->info.num_color_channels = 1;
302 		break;
303 
304 	case VIPS_INTERPRETATION_sRGB:
305 	case VIPS_INTERPRETATION_scRGB:
306 	case VIPS_INTERPRETATION_RGB16:
307 		jxl->info.num_color_channels = 3;
308 		break;
309 
310 	default:
311 		jxl->info.num_color_channels = save->ready->Bands;
312 	}
313 	jxl->info.num_extra_channels = VIPS_MAX( 0,
314 		save->ready->Bands - jxl->info.num_color_channels );
315 
316 	jxl->info.xsize = save->ready->Xsize;
317 	jxl->info.ysize = save->ready->Ysize;
318 	jxl->format.num_channels = save->ready->Bands;
319 	jxl->format.endianness = JXL_NATIVE_ENDIAN;
320 	jxl->format.align = 0;
321 
322 	if( vips_image_hasalpha( save->ready ) ) {
323 		jxl->info.alpha_bits = jxl->info.bits_per_sample;
324 		jxl->info.alpha_exponent_bits =
325 			jxl->info.exponent_bits_per_sample;
326 	}
327 	else {
328 		jxl->info.alpha_exponent_bits = 0;
329 		jxl->info.alpha_bits = 0;
330 	}
331 
332 	if( vips_image_get_typeof( save->ready, "stonits" ) ) {
333 		double stonits;
334 
335 		if( vips_image_get_double( save->ready, "stonits", &stonits ) )
336 			return( -1 );
337 		jxl->info.intensity_target = stonits;
338 	}
339 
340 	/* FIXME libjxl doesn't seem to have this API yet.
341 	 *
342 	if( vips_image_get_typeof( save->ready, VIPS_META_ICC_NAME ) ) {
343 		const void *data;
344 		size_t length;
345 
346 		if( vips_image_get_blob( save->ready,
347 			VIPS_META_ICC_NAME, &data, &length ) )
348 			return( -1 );
349 
350 		jxl->info.uses_original_profile = JXL_TRUE;
351 		... attach profile
352 	}
353 	else
354 		jxl->info.uses_original_profile = JXL_FALSE;
355 	 */
356 
357 	/* Remove this when libjxl gets API to attach an ICC profile.
358 	 */
359 	jxl->info.uses_original_profile = JXL_FALSE;
360 
361 	if( JxlEncoderSetBasicInfo( jxl->encoder, &jxl->info ) ) {
362 		vips_foreign_save_jxl_error( jxl, "JxlEncoderSetBasicInfo" );
363 		return( -1 );
364 	}
365 
366 	JxlColorEncodingSetToSRGB( &jxl->color_encoding,
367 		jxl->format.num_channels < 3 );
368 	if( JxlEncoderSetColorEncoding( jxl->encoder, &jxl->color_encoding ) ) {
369 		vips_foreign_save_jxl_error( jxl,
370 			"JxlEncoderSetColorEncoding" );
371 		return( -1 );
372 	}
373 
374 	/* Render the entire image in memory. libjxl seems to be missing
375 	 * tile-based write at the moment.
376 	 */
377 	if( vips_image_wio_input( save->ready ) )
378 		return( -1 );
379 
380 	options = JxlEncoderOptionsCreate( jxl->encoder, NULL );
381 	JxlEncoderOptionsSetDecodingSpeed( options, jxl->tier );
382 	JxlEncoderOptionsSetDistance( options, jxl->distance );
383 	JxlEncoderOptionsSetEffort( options, jxl->effort );
384 	JxlEncoderOptionsSetLossless( options, jxl->lossless );
385 
386 #ifdef DEBUG
387 	vips_foreign_save_jxl_print_info( &jxl->info );
388 	vips_foreign_save_jxl_print_format( &jxl->format );
389 	printf( "JxlEncoderOptions:\n" );
390 	printf( "    tier = %d\n", jxl->tier );
391 	printf( "    distance = %g\n", jxl->distance );
392 	printf( "    effort = %d\n", jxl->effort );
393 	printf( "    lossless = %d\n", jxl->lossless );
394 #endif /*DEBUG*/
395 
396 	if( JxlEncoderAddImageFrame( options, &jxl->format,
397 		VIPS_IMAGE_ADDR( save->ready, 0, 0 ),
398 		VIPS_IMAGE_SIZEOF_IMAGE( save->ready ) ) ) {
399 		vips_foreign_save_jxl_error( jxl, "JxlEncoderAddImageFrame" );
400 		return( -1 );
401 	}
402 
403 	do {
404 		uint8_t *out;
405 		size_t avail_out;
406 
407 		out = jxl->output_buffer;
408 		avail_out = OUTPUT_BUFFER_SIZE;
409 		status = JxlEncoderProcessOutput( jxl->encoder,
410 			&out, &avail_out );
411 		switch( status ) {
412 		case JXL_ENC_SUCCESS:
413 		case JXL_ENC_NEED_MORE_OUTPUT:
414 			if( vips_target_write( jxl->target,
415 				jxl->output_buffer,
416 				OUTPUT_BUFFER_SIZE - avail_out ) )
417 				return( -1 );
418 			break;
419 
420 		default:
421 			vips_foreign_save_jxl_error( jxl,
422 				"JxlEncoderProcessOutput" );
423 #ifdef DEBUG
424 			vips_foreign_save_jxl_print_status( status );
425 #endif /*DEBUG*/
426 			return( -1 );
427 		}
428 	} while( status != JXL_ENC_SUCCESS );
429 
430 	vips_target_finish( jxl->target );
431 
432 	return( 0 );
433 }
434 
435 /* Save a bit of typing.
436  */
437 #define UC VIPS_FORMAT_UCHAR
438 #define US VIPS_FORMAT_USHORT
439 #define UI VIPS_FORMAT_UINT
440 #define F VIPS_FORMAT_FLOAT
441 
442 /* Type promotion for save ... unsigned ints + float + double.
443  */
444 static int bandfmt_jpeg[10] = {
445      /* UC   C  US   S  UI   I  F  X  D DX */
446 	UC, UC, US, US, UI, UI, F, F, F, F
447 };
448 
449 static void
vips_foreign_save_jxl_class_init(VipsForeignSaveJxlClass * class)450 vips_foreign_save_jxl_class_init( VipsForeignSaveJxlClass *class )
451 {
452 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
453 	VipsObjectClass *object_class = (VipsObjectClass *) class;
454 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
455 	VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
456 
457 	gobject_class->dispose = vips_foreign_save_jxl_dispose;
458 	gobject_class->set_property = vips_object_set_property;
459 	gobject_class->get_property = vips_object_get_property;
460 
461 	object_class->nickname = "jxlsave_base";
462 	object_class->description = _( "save image in JPEG-XL format" );
463 	object_class->build = vips_foreign_save_jxl_build;
464 
465 	foreign_class->suffs = vips__jxl_suffs;
466 
467 	save_class->saveable = VIPS_SAVEABLE_ANY;
468 	save_class->format_table = bandfmt_jpeg;
469 
470 	VIPS_ARG_INT( class, "tier", 10,
471 		_( "Tier" ),
472 		_( "Decode speed tier" ),
473 		VIPS_ARGUMENT_OPTIONAL_INPUT,
474 		G_STRUCT_OFFSET( VipsForeignSaveJxl, tier ),
475 		0, 4, 0 );
476 
477 	VIPS_ARG_DOUBLE( class, "distance", 11,
478 		_( "Distance" ),
479 		_( "Target butteraugli distance" ),
480 		VIPS_ARGUMENT_OPTIONAL_INPUT,
481 		G_STRUCT_OFFSET( VipsForeignSaveJxl, distance ),
482 		0, 15, 1.0 );
483 
484 	VIPS_ARG_INT( class, "effort", 12,
485 		_( "effort" ),
486 		_( "Encoding effort" ),
487 		VIPS_ARGUMENT_OPTIONAL_INPUT,
488 		G_STRUCT_OFFSET( VipsForeignSaveJxl, effort ),
489 		3, 9, 7 );
490 
491 	VIPS_ARG_BOOL( class, "lossless", 13,
492 		_( "Lossless" ),
493 		_( "Enable lossless compression" ),
494 		VIPS_ARGUMENT_OPTIONAL_INPUT,
495 		G_STRUCT_OFFSET( VipsForeignSaveJxl, lossless ),
496 		FALSE );
497 
498 	VIPS_ARG_INT( class, "Q", 14,
499 		_( "Q" ),
500 		_( "Quality factor" ),
501 		VIPS_ARGUMENT_OPTIONAL_INPUT,
502 		G_STRUCT_OFFSET( VipsForeignSaveJxl, Q ),
503 		0, 100, 75 );
504 
505 }
506 
507 static void
vips_foreign_save_jxl_init(VipsForeignSaveJxl * jxl)508 vips_foreign_save_jxl_init( VipsForeignSaveJxl *jxl )
509 {
510 	jxl->tier = 0;
511 	jxl->distance = 1.0;
512 	jxl->effort = 7;
513 	jxl->lossless = FALSE;
514 	jxl->Q = 75;
515 }
516 
517 typedef struct _VipsForeignSaveJxlFile {
518 	VipsForeignSaveJxl parent_object;
519 
520 	/* Filename for save.
521 	 */
522 	char *filename;
523 
524 } VipsForeignSaveJxlFile;
525 
526 typedef VipsForeignSaveJxlClass VipsForeignSaveJxlFileClass;
527 
528 G_DEFINE_TYPE( VipsForeignSaveJxlFile, vips_foreign_save_jxl_file,
529 	vips_foreign_save_jxl_get_type() );
530 
531 static int
vips_foreign_save_jxl_file_build(VipsObject * object)532 vips_foreign_save_jxl_file_build( VipsObject *object )
533 {
534 	VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
535 	VipsForeignSaveJxlFile *file = (VipsForeignSaveJxlFile *) object;
536 
537 	if( !(jxl->target = vips_target_new_to_file( file->filename )) )
538 		return( -1 );
539 
540 	if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_file_parent_class )->
541 		build( object ) )
542 		return( -1 );
543 
544 	return( 0 );
545 }
546 
547 static void
vips_foreign_save_jxl_file_class_init(VipsForeignSaveJxlFileClass * class)548 vips_foreign_save_jxl_file_class_init( VipsForeignSaveJxlFileClass *class )
549 {
550 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
551 	VipsObjectClass *object_class = (VipsObjectClass *) class;
552 
553 	gobject_class->set_property = vips_object_set_property;
554 	gobject_class->get_property = vips_object_get_property;
555 
556 	object_class->nickname = "jxlsave";
557 	object_class->build = vips_foreign_save_jxl_file_build;
558 
559 	VIPS_ARG_STRING( class, "filename", 1,
560 		_( "Filename" ),
561 		_( "Filename to load from" ),
562 		VIPS_ARGUMENT_REQUIRED_INPUT,
563 		G_STRUCT_OFFSET( VipsForeignSaveJxlFile, filename ),
564 		NULL );
565 
566 }
567 
568 static void
vips_foreign_save_jxl_file_init(VipsForeignSaveJxlFile * file)569 vips_foreign_save_jxl_file_init( VipsForeignSaveJxlFile *file )
570 {
571 }
572 
573 typedef struct _VipsForeignSaveJxlBuffer {
574 	VipsForeignSaveJxl parent_object;
575 
576 	/* Save to a buffer.
577 	 */
578 	VipsArea *buf;
579 
580 } VipsForeignSaveJxlBuffer;
581 
582 typedef VipsForeignSaveJxlClass VipsForeignSaveJxlBufferClass;
583 
584 G_DEFINE_TYPE( VipsForeignSaveJxlBuffer, vips_foreign_save_jxl_buffer,
585 	vips_foreign_save_jxl_get_type() );
586 
587 static int
vips_foreign_save_jxl_buffer_build(VipsObject * object)588 vips_foreign_save_jxl_buffer_build( VipsObject *object )
589 {
590 	VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
591 	VipsForeignSaveJxlBuffer *buffer =
592 		(VipsForeignSaveJxlBuffer *) object;
593 
594 	VipsBlob *blob;
595 
596 	if( !(jxl->target = vips_target_new_to_memory()) )
597 		return( -1 );
598 
599 	if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_buffer_parent_class )->
600 		build( object ) )
601 		return( -1 );
602 
603 	g_object_get( jxl->target, "blob", &blob, NULL );
604 	g_object_set( buffer, "buffer", blob, NULL );
605 	vips_area_unref( VIPS_AREA( blob ) );
606 
607 	return( 0 );
608 }
609 
610 static void
vips_foreign_save_jxl_buffer_class_init(VipsForeignSaveJxlBufferClass * class)611 vips_foreign_save_jxl_buffer_class_init(
612 	VipsForeignSaveJxlBufferClass *class )
613 {
614 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
615 	VipsObjectClass *object_class = (VipsObjectClass *) class;
616 
617 	gobject_class->set_property = vips_object_set_property;
618 	gobject_class->get_property = vips_object_get_property;
619 
620 	object_class->nickname = "jxlsave_buffer";
621 	object_class->build = vips_foreign_save_jxl_buffer_build;
622 
623 	VIPS_ARG_BOXED( class, "buffer", 1,
624 		_( "Buffer" ),
625 		_( "Buffer to save to" ),
626 		VIPS_ARGUMENT_REQUIRED_OUTPUT,
627 		G_STRUCT_OFFSET( VipsForeignSaveJxlBuffer, buf ),
628 		VIPS_TYPE_BLOB );
629 
630 }
631 
632 static void
vips_foreign_save_jxl_buffer_init(VipsForeignSaveJxlBuffer * buffer)633 vips_foreign_save_jxl_buffer_init( VipsForeignSaveJxlBuffer *buffer )
634 {
635 }
636 
637 typedef struct _VipsForeignSaveJxlTarget {
638 	VipsForeignSaveJxl parent_object;
639 
640 	VipsTarget *target;
641 } VipsForeignSaveJxlTarget;
642 
643 typedef VipsForeignSaveJxlClass VipsForeignSaveJxlTargetClass;
644 
645 G_DEFINE_TYPE( VipsForeignSaveJxlTarget, vips_foreign_save_jxl_target,
646 	vips_foreign_save_jxl_get_type() );
647 
648 static int
vips_foreign_save_jxl_target_build(VipsObject * object)649 vips_foreign_save_jxl_target_build( VipsObject *object )
650 {
651 	VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
652 	VipsForeignSaveJxlTarget *target =
653 		(VipsForeignSaveJxlTarget *) object;
654 
655 	if( target->target ) {
656 		jxl->target = target->target;
657 		g_object_ref( jxl->target );
658 	}
659 
660 	if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_target_parent_class )->
661 		build( object ) )
662 		return( -1 );
663 
664 	return( 0 );
665 }
666 
667 static void
vips_foreign_save_jxl_target_class_init(VipsForeignSaveJxlTargetClass * class)668 vips_foreign_save_jxl_target_class_init(
669 	VipsForeignSaveJxlTargetClass *class )
670 {
671 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
672 	VipsObjectClass *object_class = (VipsObjectClass *) class;
673 
674 	gobject_class->set_property = vips_object_set_property;
675 	gobject_class->get_property = vips_object_get_property;
676 
677 	object_class->nickname = "jxlsave_target";
678 	object_class->build = vips_foreign_save_jxl_target_build;
679 
680 	VIPS_ARG_OBJECT( class, "target", 1,
681 		_( "Target" ),
682 		_( "Target to save to" ),
683 		VIPS_ARGUMENT_REQUIRED_INPUT,
684 		G_STRUCT_OFFSET( VipsForeignSaveJxlTarget, target ),
685 		VIPS_TYPE_TARGET );
686 
687 }
688 
689 static void
vips_foreign_save_jxl_target_init(VipsForeignSaveJxlTarget * target)690 vips_foreign_save_jxl_target_init( VipsForeignSaveJxlTarget *target )
691 {
692 }
693 
694 #endif /*HAVE_LIBJXL*/
695 
696 /* The C API wrappers are defined in foreign.c.
697  */
698