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