1 /* save to heif
2 *
3 * 5/7/18
4 * - from niftisave.c
5 * 3/7/19 [lovell]
6 * - add "compression" option
7 * 1/9/19 [meyermarcel]
8 * - save alpha when necessary
9 * 15/3/20
10 * - revise for new VipsTarget API
11 * 14/2/21 kleisauke
12 * - move GObject part to vips2heif.c
13 * 30/7/21
14 * - rename "speed" as "effort" for consistency with other savers
15 */
16
17 /*
18
19 This file is part of VIPS.
20
21 VIPS is free software; you can redistribute it and/or modify
22 it under the terms of the GNU Lesser General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
25
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU Lesser General Public License for more details.
30
31 You should have received a copy of the GNU Lesser General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
34 02110-1301 USA
35
36 */
37
38 /*
39
40 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
41
42 */
43
44 /*
45 #define DEBUG_VERBOSE
46 #define DEBUG
47 */
48
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif /*HAVE_CONFIG_H*/
52 #include <vips/intl.h>
53
54 #ifdef HAVE_HEIF_ENCODER
55
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59
60 #include <vips/vips.h>
61 #include <vips/internal.h>
62
63 #include "pforeign.h"
64
65 #include <libheif/heif.h>
66
67 typedef struct _VipsForeignSaveHeif {
68 VipsForeignSave parent_object;
69
70 /* Where to write (set by subclasses).
71 */
72 VipsTarget *target;
73
74 /* Coding quality factor (1 - 100).
75 */
76 int Q;
77
78 /* Lossless compression.
79 */
80 gboolean lossless;
81
82 /* Compression format
83 */
84 VipsForeignHeifCompression compression;
85
86 /* CPU effort (0 - 9).
87 */
88 int effort;
89
90 /* Chroma subsampling.
91 */
92 VipsForeignSubsample subsample_mode;
93
94 /* The image we save. This is a copy of save->ready since we need to
95 * be able to update the metadata.
96 */
97 VipsImage *image;
98
99 int page_width;
100 int page_height;
101 int n_pages;
102
103 struct heif_context *ctx;
104 struct heif_encoder *encoder;
105
106 /* The current page we are writing.
107 */
108 struct heif_image_handle *handle;
109
110 /* The current page in memory which we build as we scan down the
111 * image.
112 */
113 struct heif_image *img;
114
115 /* The libheif memory area we fill with pixels from the libvips
116 * pipe.
117 */
118 uint8_t *data;
119 int stride;
120
121 /* Deprecated ... this is now called effort for consistency with the
122 * other encoders.
123 */
124 int speed;
125
126 } VipsForeignSaveHeif;
127
128 typedef VipsForeignSaveClass VipsForeignSaveHeifClass;
129
130 /* Defined in heif2vips.c
131 */
132 void vips__heif_error( struct heif_error *error );
133 void vips__heif_image_print( struct heif_image *img );
134
135 G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveHeif, vips_foreign_save_heif,
136 VIPS_TYPE_FOREIGN_SAVE );
137
138 static void
vips_foreign_save_heif_dispose(GObject * gobject)139 vips_foreign_save_heif_dispose( GObject *gobject )
140 {
141 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) gobject;
142
143 VIPS_UNREF( heif->target );
144 VIPS_UNREF( heif->image );
145 VIPS_FREEF( heif_image_release, heif->img );
146 VIPS_FREEF( heif_image_handle_release, heif->handle );
147 VIPS_FREEF( heif_encoder_release, heif->encoder );
148 VIPS_FREEF( heif_context_free, heif->ctx );
149
150 G_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
151 dispose( gobject );
152 }
153
154 typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
155 const struct heif_image_handle *,
156 const void *, int );
157
158 struct _VipsForeignSaveHeifMetadata {
159 const char *name;
160 libheif_metadata_fn saver;
161 } libheif_metadata[] = {
162 { VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
163 { VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
164 };
165
166 static int
vips_foreign_save_heif_write_metadata(VipsForeignSaveHeif * heif)167 vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
168 {
169 int i;
170 struct heif_error error;
171
172 /* Rebuild exif from tags, if we'll be saving it.
173 */
174 if( vips_image_get_typeof( heif->image, VIPS_META_EXIF_NAME ) )
175 if( vips__exif_update( heif->image ) )
176 return( -1 );
177
178 for( i = 0; i < VIPS_NUMBER( libheif_metadata ); i++ )
179 if( vips_image_get_typeof( heif->image,
180 libheif_metadata[i].name ) ) {
181 const void *data;
182 size_t length;
183
184 #ifdef DEBUG
185 printf( "attaching %s ..\n",
186 libheif_metadata[i].name );
187 #endif /*DEBUG*/
188
189 if( vips_image_get_blob( heif->image,
190 libheif_metadata[i].name, &data, &length ) )
191 return( -1 );
192
193 error = libheif_metadata[i].saver( heif->ctx,
194 heif->handle, data, length );
195 if( error.code ) {
196 vips__heif_error( &error );
197 return( -1 );
198 }
199 }
200
201 return( 0 );
202 }
203
204 static int
vips_foreign_save_heif_write_page(VipsForeignSaveHeif * heif,int page)205 vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
206 {
207 VipsForeignSave *save = (VipsForeignSave *) heif;
208
209 struct heif_error error;
210 struct heif_encoding_options *options;
211
212 #ifdef HAVE_HEIF_COLOR_PROFILE
213 if( !save->strip &&
214 vips_image_get_typeof( heif->image, VIPS_META_ICC_NAME ) ) {
215 const void *data;
216 size_t length;
217
218 #ifdef DEBUG
219 printf( "attaching profile ..\n" );
220 #endif /*DEBUG*/
221
222 if( vips_image_get_blob( heif->image,
223 VIPS_META_ICC_NAME, &data, &length ) )
224 return( -1 );
225
226 /* FIXME .. also see heif_image_set_nclx_color_profile()
227 */
228 error = heif_image_set_raw_color_profile( heif->img,
229 "rICC", data, length );
230 if( error.code ) {
231 vips__heif_error( &error );
232 return( -1 );
233 }
234 }
235 #endif /*HAVE_HEIF_COLOR_PROFILE*/
236
237 options = heif_encoding_options_alloc();
238 if( vips_image_hasalpha( heif->image ) )
239 options->save_alpha_channel = 1;
240
241 #ifdef DEBUG
242 printf( "encoding ..\n" );
243 #endif /*DEBUG*/
244 error = heif_context_encode_image( heif->ctx,
245 heif->img, heif->encoder, options, &heif->handle );
246
247 heif_encoding_options_free( options );
248
249 if( error.code ) {
250 vips__heif_error( &error );
251 return( -1 );
252 }
253
254 if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
255 int primary;
256
257 if( vips_image_get_int( heif->image,
258 "heif-primary", &primary ) )
259 return( -1 );
260
261 if( page == primary ) {
262 error = heif_context_set_primary_image( heif->ctx,
263 heif->handle );
264 if( error.code ) {
265 vips__heif_error( &error );
266 return( -1 );
267 }
268 }
269 }
270
271 if( !save->strip &&
272 vips_foreign_save_heif_write_metadata( heif ) )
273 return( -1 );
274
275 VIPS_FREEF( heif_image_handle_release, heif->handle );
276
277 return( 0 );
278 }
279
280 static int
vips_foreign_save_heif_write_block(VipsRegion * region,VipsRect * area,void * a)281 vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
282 void *a )
283 {
284 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) a;
285
286 int y;
287
288 #ifdef DEBUG
289 printf( "vips_foreign_save_heif_write_block: y = %d\n", area->top );
290 #endif /*DEBUG*/
291
292 /* Copy a line at a time into our output image, write each time the
293 * image fills.
294 */
295 for( y = 0; y < area->height; y++ ) {
296 /* Y in page.
297 */
298 int page = (area->top + y) / heif->page_height;
299 int line = (area->top + y) % heif->page_height;
300
301 VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y );
302 VipsPel *q = heif->data + line * heif->stride;
303
304 memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( region->im ) );
305
306 /* Did we just write the final line? Write as a new page
307 * into the output.
308 */
309 if( line == heif->page_height - 1 )
310 if( vips_foreign_save_heif_write_page( heif, page ) )
311 return( -1 );
312 }
313
314 return( 0 );
315 }
316
317 struct heif_error
vips_foreign_save_heif_write(struct heif_context * ctx,const void * data,size_t length,void * userdata)318 vips_foreign_save_heif_write( struct heif_context *ctx,
319 const void *data, size_t length, void *userdata )
320 {
321 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
322
323 struct heif_error error;
324
325 error.code = 0;
326 if( vips_target_write( heif->target, data, length ) )
327 error.code = -1;
328
329 return( error );
330 }
331
332 static int
vips_foreign_save_heif_build(VipsObject * object)333 vips_foreign_save_heif_build( VipsObject *object )
334 {
335 VipsForeignSave *save = (VipsForeignSave *) object;
336 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
337
338 struct heif_error error;
339 struct heif_writer writer;
340 char *chroma;
341
342 if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
343 build( object ) )
344 return( -1 );
345
346 /* If the old, deprecated "speed" param is being used and the new
347 * "effort" param is not, use speed to init effort.
348 */
349 if( vips_object_argument_isset( object, "speed" ) &&
350 !vips_object_argument_isset( object, "effort" ) )
351 heif->effort = 9 - heif->speed;
352
353 /* Make a copy of the image in case we modify the metadata eg. for
354 * exif_update.
355 */
356 if( vips_copy( save->ready, &heif->image, NULL ) )
357 return( -1 );
358
359 error = heif_context_get_encoder_for_format( heif->ctx,
360 (enum heif_compression_format) heif->compression,
361 &heif->encoder );
362 if( error.code ) {
363 if( error.code == heif_error_Unsupported_filetype )
364 vips_error( "heifsave",
365 "%s", _( "Unsupported compression" ) );
366 else
367 vips__heif_error( &error );
368
369 return( -1 );
370 }
371
372 error = heif_encoder_set_lossy_quality( heif->encoder, heif->Q );
373 if( error.code ) {
374 vips__heif_error( &error );
375 return( -1 );
376 }
377
378 error = heif_encoder_set_lossless( heif->encoder, heif->lossless );
379 if( error.code ) {
380 vips__heif_error( &error );
381 return( -1 );
382 }
383
384 error = heif_encoder_set_parameter_integer( heif->encoder,
385 "speed", 9 - heif->effort );
386 if( error.code &&
387 error.subcode != heif_suberror_Unsupported_parameter ) {
388 vips__heif_error( &error );
389 return( -1 );
390 }
391
392 chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
393 ( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
394 heif->Q >= 90 ) ? "444" : "420";
395 error = heif_encoder_set_parameter_string( heif->encoder,
396 "chroma", chroma );
397 if( error.code &&
398 error.subcode != heif_suberror_Unsupported_parameter ) {
399 vips__heif_error( &error );
400 return( -1 );
401 }
402
403 /* TODO .. support extra per-encoder params with
404 * heif_encoder_list_parameters().
405 */
406
407 heif->page_width = heif->image->Xsize;
408 heif->page_height = vips_image_get_page_height( heif->image );
409 heif->n_pages = heif->image->Ysize / heif->page_height;
410
411 /* Make a heif image the size of a page. We send sink_disc() output
412 * here and write a frame each time it fills.
413 */
414 #ifdef DEBUG
415 printf( "vips_foreign_save_heif_build:\n" );
416 printf( "\twidth = %d\n", heif->page_width );
417 printf( "\theight = %d\n", heif->page_height );
418 printf( "\talpha = %d\n", vips_image_hasalpha( heif->image ) );
419 #endif /*DEBUG*/
420 error = heif_image_create( heif->page_width, heif->page_height,
421 heif_colorspace_RGB,
422 vips_image_hasalpha( heif->image ) ?
423 heif_chroma_interleaved_RGBA :
424 heif_chroma_interleaved_RGB,
425 &heif->img );
426 if( error.code ) {
427 vips__heif_error( &error );
428 return( -1 );
429 }
430
431 error = heif_image_add_plane( heif->img, heif_channel_interleaved,
432 heif->page_width, heif->page_height,
433 vips_image_hasalpha( heif->image ) ? 32 : 24 );
434 if( error.code ) {
435 vips__heif_error( &error );
436 return( -1 );
437 }
438
439 #ifdef DEBUG
440 vips__heif_image_print( heif->img );
441 #endif /*DEBUG*/
442
443 heif->data = heif_image_get_plane( heif->img,
444 heif_channel_interleaved, &heif->stride );
445
446 /* Write data.
447 */
448 if( vips_sink_disc( heif->image,
449 vips_foreign_save_heif_write_block, heif ) )
450 return( -1 );
451
452 /* This has to come right at the end :-( so there's no support for
453 * incremental writes.
454 */
455 writer.writer_api_version = 1;
456 writer.write = vips_foreign_save_heif_write;
457 error = heif_context_write( heif->ctx, &writer, heif );
458 if( error.code ) {
459 vips__heif_error( &error );
460 return( -1 );
461 }
462
463 vips_target_finish( heif->target );
464
465 return( 0 );
466 }
467
468 /* Save a bit of typing.
469 */
470 #define UC VIPS_FORMAT_UCHAR
471
472 static int vips_heif_bandfmt[10] = {
473 /* UC C US S UI I F X D DX */
474 UC, UC, UC, UC, UC, UC, UC, UC, UC, UC
475 };
476
477 static void
vips_foreign_save_heif_class_init(VipsForeignSaveHeifClass * class)478 vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
479 {
480 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
481 VipsObjectClass *object_class = (VipsObjectClass *) class;
482 VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
483
484 gobject_class->dispose = vips_foreign_save_heif_dispose;
485 gobject_class->set_property = vips_object_set_property;
486 gobject_class->get_property = vips_object_get_property;
487
488 object_class->nickname = "heifsave_base";
489 object_class->description = _( "save image in HEIF format" );
490 object_class->build = vips_foreign_save_heif_build;
491
492 save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
493 save_class->format_table = vips_heif_bandfmt;
494
495 VIPS_ARG_INT( class, "Q", 10,
496 _( "Q" ),
497 _( "Q factor" ),
498 VIPS_ARGUMENT_OPTIONAL_INPUT,
499 G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
500 1, 100, 50 );
501
502 VIPS_ARG_BOOL( class, "lossless", 13,
503 _( "Lossless" ),
504 _( "Enable lossless compression" ),
505 VIPS_ARGUMENT_OPTIONAL_INPUT,
506 G_STRUCT_OFFSET( VipsForeignSaveHeif, lossless ),
507 FALSE );
508
509 VIPS_ARG_ENUM( class, "compression", 14,
510 _( "Compression" ),
511 _( "Compression format" ),
512 VIPS_ARGUMENT_OPTIONAL_INPUT,
513 G_STRUCT_OFFSET( VipsForeignSaveHeif, compression ),
514 VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
515 VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
516
517 VIPS_ARG_INT( class, "effort", 15,
518 _( "Effort" ),
519 _( "CPU effort" ),
520 VIPS_ARGUMENT_OPTIONAL_INPUT,
521 G_STRUCT_OFFSET( VipsForeignSaveHeif, effort ),
522 0, 9, 4 );
523
524 VIPS_ARG_ENUM( class, "subsample_mode", 16,
525 _( "Subsample mode" ),
526 _( "Select chroma subsample operation mode" ),
527 VIPS_ARGUMENT_OPTIONAL_INPUT,
528 G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
529 VIPS_TYPE_FOREIGN_SUBSAMPLE,
530 VIPS_FOREIGN_SUBSAMPLE_AUTO );
531
532 VIPS_ARG_INT( class, "speed", 17,
533 _( "Speed" ),
534 _( "CPU effort" ),
535 VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
536 G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
537 0, 9, 5 );
538
539 }
540
541 static void
vips_foreign_save_heif_init(VipsForeignSaveHeif * heif)542 vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
543 {
544 heif->ctx = heif_context_alloc();
545 heif->Q = 50;
546 heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
547 heif->effort = 4;
548 heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
549
550 /* Deprecated.
551 */
552 heif->speed = 5;
553 }
554
555 typedef struct _VipsForeignSaveHeifFile {
556 VipsForeignSaveHeif parent_object;
557
558 /* Filename for save.
559 */
560 char *filename;
561
562 } VipsForeignSaveHeifFile;
563
564 typedef VipsForeignSaveHeifClass VipsForeignSaveHeifFileClass;
565
566 G_DEFINE_TYPE( VipsForeignSaveHeifFile, vips_foreign_save_heif_file,
567 vips_foreign_save_heif_get_type() );
568
569 static int
vips_foreign_save_heif_file_build(VipsObject * object)570 vips_foreign_save_heif_file_build( VipsObject *object )
571 {
572 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
573 VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
574
575 if( !(heif->target = vips_target_new_to_file( file->filename )) )
576 return( -1 );
577
578 if( vips_iscasepostfix( file->filename, ".avif" ) )
579 heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
580
581 if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )->
582 build( object ) )
583 return( -1 );
584
585 return( 0 );
586 }
587
588 static void
vips_foreign_save_heif_file_class_init(VipsForeignSaveHeifFileClass * class)589 vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *class )
590 {
591 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
592 VipsObjectClass *object_class = (VipsObjectClass *) class;
593 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
594
595 gobject_class->set_property = vips_object_set_property;
596 gobject_class->get_property = vips_object_get_property;
597
598 object_class->nickname = "heifsave";
599 object_class->build = vips_foreign_save_heif_file_build;
600
601 foreign_class->suffs = vips__heif_suffs;
602
603 VIPS_ARG_STRING( class, "filename", 1,
604 _( "Filename" ),
605 _( "Filename to save to" ),
606 VIPS_ARGUMENT_REQUIRED_INPUT,
607 G_STRUCT_OFFSET( VipsForeignSaveHeifFile, filename ),
608 NULL );
609
610 }
611
612 static void
vips_foreign_save_heif_file_init(VipsForeignSaveHeifFile * file)613 vips_foreign_save_heif_file_init( VipsForeignSaveHeifFile *file )
614 {
615 }
616
617 typedef struct _VipsForeignSaveHeifBuffer {
618 VipsForeignSaveHeif parent_object;
619
620 /* Save to a buffer.
621 */
622 VipsArea *buf;
623
624 } VipsForeignSaveHeifBuffer;
625
626 typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
627
628 G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer,
629 vips_foreign_save_heif_get_type() );
630
631 static int
vips_foreign_save_heif_buffer_build(VipsObject * object)632 vips_foreign_save_heif_buffer_build( VipsObject *object )
633 {
634 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
635 VipsForeignSaveHeifBuffer *buffer =
636 (VipsForeignSaveHeifBuffer *) object;
637
638 VipsBlob *blob;
639
640 if( !(heif->target = vips_target_new_to_memory()) )
641 return( -1 );
642
643 if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_buffer_parent_class )->
644 build( object ) )
645 return( -1 );
646
647 g_object_get( heif->target, "blob", &blob, NULL );
648 g_object_set( buffer, "buffer", blob, NULL );
649 vips_area_unref( VIPS_AREA( blob ) );
650
651 return( 0 );
652 }
653
654 static void
vips_foreign_save_heif_buffer_class_init(VipsForeignSaveHeifBufferClass * class)655 vips_foreign_save_heif_buffer_class_init(
656 VipsForeignSaveHeifBufferClass *class )
657 {
658 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
659 VipsObjectClass *object_class = (VipsObjectClass *) class;
660 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
661
662 gobject_class->set_property = vips_object_set_property;
663 gobject_class->get_property = vips_object_get_property;
664
665 object_class->nickname = "heifsave_buffer";
666 object_class->build = vips_foreign_save_heif_buffer_build;
667
668 foreign_class->suffs = vips__heic_suffs;
669
670 VIPS_ARG_BOXED( class, "buffer", 1,
671 _( "Buffer" ),
672 _( "Buffer to save to" ),
673 VIPS_ARGUMENT_REQUIRED_OUTPUT,
674 G_STRUCT_OFFSET( VipsForeignSaveHeifBuffer, buf ),
675 VIPS_TYPE_BLOB );
676
677 }
678
679 static void
vips_foreign_save_heif_buffer_init(VipsForeignSaveHeifBuffer * buffer)680 vips_foreign_save_heif_buffer_init( VipsForeignSaveHeifBuffer *buffer )
681 {
682 }
683
684 typedef struct _VipsForeignSaveHeifTarget {
685 VipsForeignSaveHeif parent_object;
686
687 VipsTarget *target;
688 } VipsForeignSaveHeifTarget;
689
690 typedef VipsForeignSaveHeifClass VipsForeignSaveHeifTargetClass;
691
692 G_DEFINE_TYPE( VipsForeignSaveHeifTarget, vips_foreign_save_heif_target,
693 vips_foreign_save_heif_get_type() );
694
695 static int
vips_foreign_save_heif_target_build(VipsObject * object)696 vips_foreign_save_heif_target_build( VipsObject *object )
697 {
698 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
699 VipsForeignSaveHeifTarget *target =
700 (VipsForeignSaveHeifTarget *) object;
701
702 if( target->target ) {
703 heif->target = target->target;
704 g_object_ref( heif->target );
705 }
706
707 if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_target_parent_class )->
708 build( object ) )
709 return( -1 );
710
711 return( 0 );
712 }
713
714 static void
vips_foreign_save_heif_target_class_init(VipsForeignSaveHeifTargetClass * class)715 vips_foreign_save_heif_target_class_init(
716 VipsForeignSaveHeifTargetClass *class )
717 {
718 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
719 VipsObjectClass *object_class = (VipsObjectClass *) class;
720 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
721
722 gobject_class->set_property = vips_object_set_property;
723 gobject_class->get_property = vips_object_get_property;
724
725 object_class->nickname = "heifsave_target";
726 object_class->build = vips_foreign_save_heif_target_build;
727
728 foreign_class->suffs = vips__heic_suffs;
729
730 VIPS_ARG_OBJECT( class, "target", 1,
731 _( "Target" ),
732 _( "Target to save to" ),
733 VIPS_ARGUMENT_REQUIRED_INPUT,
734 G_STRUCT_OFFSET( VipsForeignSaveHeifTarget, target ),
735 VIPS_TYPE_TARGET );
736
737 }
738
739 static void
vips_foreign_save_heif_target_init(VipsForeignSaveHeifTarget * target)740 vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
741 {
742 }
743
744 typedef VipsForeignSaveHeifTarget VipsForeignSaveAvifTarget;
745 typedef VipsForeignSaveHeifTargetClass VipsForeignSaveAvifTargetClass;
746
747 G_DEFINE_TYPE( VipsForeignSaveAvifTarget, vips_foreign_save_avif_target,
748 vips_foreign_save_heif_target_get_type() );
749
750 static void
vips_foreign_save_avif_target_class_init(VipsForeignSaveAvifTargetClass * class)751 vips_foreign_save_avif_target_class_init(
752 VipsForeignSaveAvifTargetClass *class )
753 {
754 VipsObjectClass *object_class = (VipsObjectClass *) class;
755 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
756 VipsOperationClass *operation_class = (VipsOperationClass *) class;
757
758 object_class->nickname = "avifsave_target";
759 object_class->description = _( "save image in AVIF format" );
760
761 foreign_class->suffs = vips__avif_suffs;
762
763 /* Hide from UI.
764 */
765 operation_class->flags = VIPS_OPERATION_DEPRECATED;
766 }
767
768 static void
vips_foreign_save_avif_target_init(VipsForeignSaveAvifTarget * target)769 vips_foreign_save_avif_target_init( VipsForeignSaveAvifTarget *target )
770 {
771 VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) target;
772
773 heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
774 }
775
776 #endif /*HAVE_HEIF_ENCODER*/
777
778 /* The C API wrappers are defined in foreign.c.
779 */
780