1 /* Common functions for interfacing with ImageMagick.
2 *
3 * 22/12/17 dlemstra
4 *
5 * 24/7/18
6 * - add the sniffer
7 * 16/10/20 [bfriesen]
8 * - set matte and depth appropriately for GM in magick_import_pixels()
9 */
10
11 /*
12
13 This file is part of VIPS.
14
15 VIPS is free software; you can redistribute it and/or modify
16 it under the terms of the GNU Lesser General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Lesser General Public License for more details.
24
25 You should have received a copy of the GNU Lesser General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 02110-1301 USA
29
30 */
31
32 /*
33
34 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
35
36 */
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif /*HAVE_CONFIG_H*/
41 #include <vips/intl.h>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <vips/vips.h>
48 #include <vips/internal.h>
49
50 #include "pforeign.h"
51 #include "magick.h"
52
53 #if defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7)
54
55 /* ImageMagick can't detect some formats, like ICO and TGA, by examining the
56 * contents -- ico.c and tga.c simply do not have recognisers.
57 *
58 * For these formats, do the detection ourselves.
59 * Return an IM format specifier, or NULL to let IM do the detection.
60 *
61 * For sniffing TGAs, we check that there is at least enough room for the
62 * header and that the preamble contains valid values:
63 *
64 * -----------------------------------------------------------
65 * |0x00 | 0-255 idlength, skip |
66 * |0x01 | 0-1, color map present |
67 * |0x02 | Any of (0, 1, 2, 3, 9, 10, 11), Image type |
68 * -----------------------------------------------------------
69 *
70 * References:
71 * * https://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf
72 * * http://www.paulbourke.net/dataformats/tga/
73 * * https://en.wikipedia.org/wiki/Truevision_TGA#Technical_details
74 */
75 static const char *
magick_sniff(const unsigned char * bytes,size_t length)76 magick_sniff( const unsigned char *bytes, size_t length )
77 {
78 if( length >= 4 &&
79 bytes[0] == 0 &&
80 bytes[1] == 0 &&
81 bytes[2] == 1 &&
82 bytes[3] == 0 )
83 return( "ICO" );
84
85 if( length >= 18 &&
86 (bytes[1] == 0 ||
87 bytes[1] == 1) &&
88 (bytes[2] == 0 ||
89 bytes[2] == 1 ||
90 bytes[2] == 2 ||
91 bytes[2] == 3 ||
92 bytes[2] == 9 ||
93 bytes[2] == 10 ||
94 bytes[2] == 11) )
95 return( "TGA" );
96
97 return( NULL );
98 }
99
100 void
magick_sniff_bytes(ImageInfo * image_info,const unsigned char * bytes,size_t length)101 magick_sniff_bytes( ImageInfo *image_info,
102 const unsigned char *bytes, size_t length )
103 {
104 const char *format;
105
106 if( (format = magick_sniff( bytes, length )) )
107 vips_strncpy( image_info->magick, format, MaxTextExtent );
108 }
109
110 void
magick_sniff_file(ImageInfo * image_info,const char * filename)111 magick_sniff_file( ImageInfo *image_info, const char *filename )
112 {
113 unsigned char bytes[256];
114 size_t length;
115
116 if( (length = vips__get_bytes( filename, bytes, 256 )) >= 4 )
117 magick_sniff_bytes( image_info, bytes, 256 );
118 }
119
120 #endif /*defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7)*/
121
122 #ifdef HAVE_MAGICK7
123
124 Image *
magick_acquire_image(const ImageInfo * image_info,ExceptionInfo * exception)125 magick_acquire_image( const ImageInfo *image_info, ExceptionInfo *exception )
126 {
127 return( AcquireImage( image_info, exception ) );
128 }
129
130 void
magick_acquire_next_image(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)131 magick_acquire_next_image( const ImageInfo *image_info, Image *image,
132 ExceptionInfo *exception )
133 {
134 AcquireNextImage( image_info, image, exception );
135 }
136
137 int
magick_set_image_size(Image * image,const size_t width,const size_t height,ExceptionInfo * exception)138 magick_set_image_size( Image *image, const size_t width, const size_t height,
139 ExceptionInfo *exception )
140 {
141 return( SetImageExtent( image, width, height, exception ) );
142 }
143
144 int
magick_import_pixels(Image * image,const ssize_t x,const ssize_t y,const size_t width,const size_t height,const char * map,const StorageType type,const void * pixels,ExceptionInfo * exception)145 magick_import_pixels( Image *image, const ssize_t x, const ssize_t y,
146 const size_t width, const size_t height, const char *map,
147 const StorageType type,const void *pixels, ExceptionInfo *exception )
148 {
149 return( ImportImagePixels( image, x, y, width, height, map,
150 type, pixels, exception ) );
151 }
152
153 void *
magick_images_to_blob(const ImageInfo * image_info,Image * images,size_t * length,ExceptionInfo * exception)154 magick_images_to_blob( const ImageInfo *image_info, Image *images,
155 size_t *length, ExceptionInfo *exception )
156 {
157 return( ImagesToBlob( image_info, images, length, exception ) );
158 }
159
160 void
magick_set_property(Image * image,const char * property,const char * value,ExceptionInfo * exception)161 magick_set_property( Image *image, const char *property, const char *value,
162 ExceptionInfo *exception )
163 {
164 (void) SetImageProperty( image, property, value, exception );
165 }
166
167 int
magick_set_profile(Image * image,const char * name,const void * data,size_t length,ExceptionInfo * exception)168 magick_set_profile( Image *image,
169 const char *name, const void *data, size_t length,
170 ExceptionInfo *exception )
171 {
172 StringInfo *string;
173 MagickBooleanType result;
174
175 string = BlobToStringInfo( data, length );
176 result = SetImageProfile( image, name, string, exception );
177 DestroyStringInfo( string );
178
179 return( result );
180 }
181
182 void *
magick_profile_map(Image * image,MagickMapProfileFn fn,void * a)183 magick_profile_map( Image *image, MagickMapProfileFn fn, void *a )
184 {
185 char *name;
186
187 ResetImageProfileIterator( image );
188 while( (name = GetNextImageProfile( image )) ) {
189 const StringInfo *profile;
190 void *data;
191 size_t length;
192 void *result;
193
194 profile = GetImageProfile( image, name );
195 data = GetStringInfoDatum( profile );
196 length = GetStringInfoLength( profile );
197 if( (result = fn( image, name, data, length, a )) )
198 return( result );
199 }
200
201 return( NULL );
202 }
203
204 ExceptionInfo *
magick_acquire_exception(void)205 magick_acquire_exception( void )
206 {
207 return( AcquireExceptionInfo() );
208 }
209
210 void
magick_destroy_exception(ExceptionInfo * exception)211 magick_destroy_exception( ExceptionInfo *exception )
212 {
213 VIPS_FREEF( DestroyExceptionInfo, exception );
214 }
215
216 void
magick_inherit_exception(ExceptionInfo * exception,Image * image)217 magick_inherit_exception( ExceptionInfo *exception, Image *image )
218 {
219 (void) exception;
220 (void) image;
221 }
222
223 void
magick_set_number_scenes(ImageInfo * image_info,int scene,int number_scenes)224 magick_set_number_scenes( ImageInfo *image_info, int scene, int number_scenes )
225 {
226 /* I can't find docs for these fields, but this seems to work.
227 */
228 char page[256];
229
230 image_info->scene = scene;
231 image_info->number_scenes = number_scenes;
232
233 /* Some IMs must have the string version set as well.
234 */
235 vips_snprintf( page, 256, "%d-%d", scene, scene + number_scenes );
236 image_info->scenes = strdup( page );
237 }
238
239 int
magick_optimize_image_layers(Image ** images,ExceptionInfo * exception)240 magick_optimize_image_layers( Image **images, ExceptionInfo *exception )
241 {
242 Image *tmp;
243
244 tmp = OptimizePlusImageLayers( *images, exception );
245
246 if( exception->severity != UndefinedException ) {
247 VIPS_FREEF( DestroyImageList, tmp );
248 return MagickFalse;
249 }
250
251 VIPS_FREEF( DestroyImageList, *images );
252 *images = tmp;
253
254 return MagickTrue;
255 }
256
257 int
magick_optimize_image_transparency(const Image * images,ExceptionInfo * exception)258 magick_optimize_image_transparency( const Image *images,
259 ExceptionInfo *exception )
260 {
261 OptimizeImageTransparency( images, exception );
262
263 return( exception->severity == UndefinedException );
264 }
265
266 /* Does a few bytes look like a file IM can handle?
267 */
268 gboolean
magick_ismagick(const unsigned char * bytes,size_t length)269 magick_ismagick( const unsigned char *bytes, size_t length )
270 {
271 char format[MagickPathExtent];
272
273 magick_genesis();
274
275 /* Try with our custom sniffers first.
276 */
277 return( magick_sniff( bytes, length ) ||
278 GetImageMagick( bytes, length, format ) );
279 }
280
281 #endif /*HAVE_MAGICK7*/
282
283 #ifdef HAVE_MAGICK6
284
285 Image *
magick_acquire_image(const ImageInfo * image_info,ExceptionInfo * exception)286 magick_acquire_image( const ImageInfo *image_info, ExceptionInfo *exception )
287 {
288 (void) exception;
289
290 #ifdef HAVE_ACQUIREIMAGE
291 return( AcquireImage( image_info ) );
292 #else /*!HAVE_ACQUIREIMAGE*/
293 /* IM5-ish and GraphicsMagick use AllocateImage().
294 */
295 return( AllocateImage( image_info ) );
296 #endif
297 }
298
299 void
magick_acquire_next_image(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)300 magick_acquire_next_image( const ImageInfo *image_info, Image *image,
301 ExceptionInfo *exception )
302 {
303 (void) exception;
304 #ifdef HAVE_ACQUIREIMAGE
305 AcquireNextImage( image_info, image );
306 #else /*!HAVE_ACQUIREIMAGE*/
307 /* IM5-ish and GraphicsMagick use AllocateNextImage().
308 */
309 AllocateNextImage( image_info, image );
310 #endif
311 }
312
313 int
magick_set_image_size(Image * image,const size_t width,const size_t height,ExceptionInfo * exception)314 magick_set_image_size( Image *image, const size_t width, const size_t height,
315 ExceptionInfo *exception )
316 {
317 #ifdef HAVE_SETIMAGEEXTENT
318 int result = SetImageExtent( image, width, height );
319
320 /* IM6 sets the exception on the image.
321 */
322 if( !result )
323 magick_inherit_exception( exception, image );
324
325 return( result );
326 #else /*!HAVE_SETIMAGEEXTENT*/
327 (void) exception;
328 image->columns = width;
329 image->rows = height;
330
331 /* imagemagick does a SyncImagePixelCache() at the end of
332 * SetImageExtent(), but GM does not really have an equivalent. Just
333 * always return True.
334 */
335 return( MagickTrue );
336 #endif /*HAVE_SETIMAGEEXTENT*/
337 }
338
339 int
magick_import_pixels(Image * image,const ssize_t x,const ssize_t y,const size_t width,const size_t height,const char * map,const StorageType type,const void * pixels,ExceptionInfo * exception)340 magick_import_pixels( Image *image, const ssize_t x, const ssize_t y,
341 const size_t width, const size_t height, const char *map,
342 const StorageType type, const void *pixels, ExceptionInfo *exception )
343 {
344 #ifdef HAVE_IMPORTIMAGEPIXELS
345 return( ImportImagePixels( image, x, y, width, height, map,
346 type, pixels ) );
347 #else /*!HAVE_IMPORTIMAGEPIXELS*/
348 Image *constitute_image;
349 unsigned int storage_type_depth;
350
351 g_assert( image );
352 g_assert( image->signature == MagickSignature );
353
354 constitute_image = ConstituteImage( width, height, map, type,
355 pixels, &image->exception );
356 if( !constitute_image )
357 return( MagickFalse );
358
359 /* image needs to inherit these fields from constitute_image.
360 */
361 switch( type ) {
362 case CharPixel:
363 storage_type_depth = sizeof( unsigned char ) * 8;
364 break;
365
366 case ShortPixel:
367 storage_type_depth = sizeof( unsigned short ) * 8;
368 break;
369
370 case IntegerPixel:
371 storage_type_depth = sizeof( unsigned short ) * 8;
372 break;
373
374 case LongPixel:
375 storage_type_depth = sizeof( unsigned long ) * 8;
376 break;
377
378 case FloatPixel:
379 storage_type_depth = sizeof( float ) * 8;
380 break;
381
382 case DoublePixel:
383 storage_type_depth = sizeof( double ) * 8;
384 break;
385
386 default:
387 storage_type_depth = QuantumDepth;
388 break;
389
390 }
391 image->depth = VIPS_MIN( storage_type_depth, QuantumDepth );
392 image->matte = constitute_image->matte;
393
394 (void) CompositeImage( image, CopyCompositeOp, constitute_image, x, y );
395
396 DestroyImage( constitute_image );
397
398 return( image->exception.severity == UndefinedException );
399 #endif /*HAVE_IMPORTIMAGEPIXELS*/
400 }
401
402 void *
magick_images_to_blob(const ImageInfo * image_info,Image * images,size_t * length,ExceptionInfo * exception)403 magick_images_to_blob( const ImageInfo *image_info, Image *images,
404 size_t *length, ExceptionInfo *exception )
405 {
406 #ifdef HAVE_IMAGESTOBLOB
407 return( ImagesToBlob( image_info, images, length, exception ) );
408 #else
409 return( ImageToBlob( image_info, images, length, exception ) );
410 #endif /*HAVE_IMAGESTOBLOB*/
411 }
412
413 void
magick_set_property(Image * image,const char * property,const char * value,ExceptionInfo * exception)414 magick_set_property( Image *image, const char *property, const char *value,
415 ExceptionInfo *exception )
416 {
417 (void) exception;
418 #ifdef HAVE_SETIMAGEPROPERTY
419 (void) SetImageProperty( image, property, value );
420 #else /*!HAVE_SETIMAGEPROPERTY*/
421 (void) SetImageAttribute( image, property, value );
422 #endif /*HAVE_SETIMAGEPROPERTY*/
423 }
424
425 int
magick_set_profile(Image * image,const char * name,const void * data,size_t length,ExceptionInfo * exception)426 magick_set_profile( Image *image,
427 const char *name, const void *data, size_t length,
428 ExceptionInfo *exception )
429 {
430 int result;
431
432 #ifdef HAVE_BLOBTOSTRINGINFO
433 StringInfo *string;
434
435 string = BlobToStringInfo( data, length );
436 result = SetImageProfile( image, name, string );
437 DestroyStringInfo( string );
438 #else /*!HAVE_BLOBTOSTRINGINFO*/
439 result = SetImageProfile( image, name, data, length );
440 #endif /*HAVE_BLOBTOSTRINGINFO*/
441
442 return( result );
443 }
444
445 void *
magick_profile_map(Image * image,MagickMapProfileFn fn,void * a)446 magick_profile_map( Image *image, MagickMapProfileFn fn, void *a )
447 {
448 const char *name;
449 const void *data;
450 size_t length;
451 void *result;
452
453 #ifdef HAVE_RESETIMAGEPROFILEITERATOR
454 ResetImageProfileIterator( image );
455 while( (name = GetNextImageProfile( image )) ) {
456 const StringInfo *profile;
457
458 profile = GetImageProfile( image, name );
459 data = GetStringInfoDatum( profile );
460 length = GetStringInfoLength( profile );
461 if( (result = fn( image, name, data, length, a )) )
462 return( result );
463 }
464 #else /*!HAVE_RESETIMAGEPROFILEITERATOR*/
465 {
466 ImageProfileIterator *iter;
467
468 iter = AllocateImageProfileIterator( image );
469 while( NextImageProfile( iter,
470 &name, (const unsigned char **) &data, &length ) ) {
471 if( (result = fn( image, name, data, length, a )) ) {
472 DeallocateImageProfileIterator( iter );
473 return( result );
474 }
475 }
476 DeallocateImageProfileIterator( iter );
477 }
478 #endif /*HAVE_RESETIMAGEPROFILEITERATOR*/
479
480 return( NULL );
481 }
482
483 ExceptionInfo *
magick_acquire_exception(void)484 magick_acquire_exception( void )
485 {
486 ExceptionInfo *exception;
487
488 #ifdef HAVE_ACQUIREEXCEPTIONINFO
489 /* IM6+
490 */
491 exception = AcquireExceptionInfo();
492 #else /*!HAVE_ACQUIREEXCEPTIONINFO*/
493 /* gm
494 */
495 exception = g_new( ExceptionInfo, 1 );
496 GetExceptionInfo( exception );
497 #endif /*HAVE_ACQUIREEXCEPTIONINFO*/
498
499 return( exception );
500 }
501
502 void
magick_destroy_exception(ExceptionInfo * exception)503 magick_destroy_exception( ExceptionInfo *exception )
504 {
505 #ifdef HAVE_ACQUIREEXCEPTIONINFO
506 /* IM6+ will free the exception in destroy.
507 */
508 VIPS_FREEF( DestroyExceptionInfo, exception );
509 #else /*!HAVE_ACQUIREEXCEPTIONINFO*/
510 /* gm and very old IM need to free the memory too.
511 */
512 if( exception ) {
513 DestroyExceptionInfo( exception );
514 g_free( exception );
515 }
516 #endif /*HAVE_ACQUIREEXCEPTIONINFO*/
517 }
518
519 void
magick_inherit_exception(ExceptionInfo * exception,Image * image)520 magick_inherit_exception( ExceptionInfo *exception, Image *image )
521 {
522 #ifdef HAVE_INHERITEXCEPTION
523 InheritException( exception, &image->exception );
524 #endif /*HAVE_INHERITEXCEPTION*/
525 }
526
527 void
magick_set_number_scenes(ImageInfo * image_info,int scene,int number_scenes)528 magick_set_number_scenes( ImageInfo *image_info, int scene, int number_scenes )
529 {
530 #ifdef HAVE_NUMBER_SCENES
531 /* I can't find docs for these fields, but this seems to work.
532 */
533 char page[256];
534
535 image_info->scene = scene;
536 image_info->number_scenes = number_scenes;
537
538 /* Some IMs must have the string version set as well.
539 */
540 vips_snprintf( page, 256, "%d-%d", scene, scene + number_scenes );
541 image_info->scenes = strdup( page );
542 #else /*!HAVE_NUMBER_SCENES*/
543 /* This works with GM 1.2.31 and probably others.
544 */
545 image_info->subimage = scene;
546 image_info->subrange = number_scenes;
547 #endif
548 }
549
550 int
magick_optimize_image_layers(Image ** images,ExceptionInfo * exception)551 magick_optimize_image_layers( Image **images, ExceptionInfo *exception )
552 {
553 #ifdef HAVE_OPTIMIZEPLUSIMAGELAYERS
554 Image *tmp;
555
556 tmp = OptimizePlusImageLayers(*images, exception );
557
558 if ( exception->severity != UndefinedException )
559 return MagickFalse;
560
561 VIPS_FREEF( DestroyImageList, *images );
562
563 *images = tmp;
564
565 return MagickTrue;
566 #else /*!HAVE_OPTIMIZEPLUSIMAGELAYERS*/
567 g_warning( "%s", _( "layer optimization is not supported by "
568 "your version of libMagick" ) );
569 return MagickTrue;
570 #endif /*HAVE_OPTIMIZEPLUSIMAGELAYERS*/
571 }
572
573 int
magick_optimize_image_transparency(const Image * images,ExceptionInfo * exception)574 magick_optimize_image_transparency( const Image *images,
575 ExceptionInfo *exception )
576 {
577 #ifdef HAVE_OPTIMIZEIMAGETRANSPARENCY
578 OptimizeImageTransparency(images, exception);
579 return ( exception->severity == UndefinedException );
580 #else /*!HAVE_OPTIMIZEIMAGETRANSPARENCY*/
581 g_warning( "%s", _( "transparency optimization is not supported by "
582 "your version of libMagick" ) );
583 return MagickTrue;
584 #endif /*HAVE_OPTIMIZEIMAGETRANSPARENCY*/
585 }
586
587 /* Does a few bytes look like a file IM can handle?
588 */
589 gboolean
magick_ismagick(const unsigned char * bytes,size_t length)590 magick_ismagick( const unsigned char *bytes, size_t length )
591 {
592 magick_genesis();
593
594 /* Try with our custom sniffers first.
595 */
596 #ifdef HAVE_GETIMAGEMAGICK3
597 {
598 char format[MaxTextExtent];
599
600 return( magick_sniff( bytes, length ) ||
601 GetImageMagick( bytes, length, format ) );
602 }
603 #else /*!HAVE_GETIMAGEMAGICK3*/
604 /* The GM one returns a static string.
605 */
606 return( magick_sniff( bytes, length ) ||
607 GetImageMagick( bytes, length ) );
608 #endif
609 }
610
611 #endif /*HAVE_MAGICK6*/
612
613 #if defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7)
614
615 void
magick_set_image_option(ImageInfo * image_info,const char * name,const char * value)616 magick_set_image_option( ImageInfo *image_info,
617 const char *name, const char *value )
618 {
619 #ifdef HAVE_SETIMAGEOPTION
620 SetImageOption( image_info, name, value );
621 #endif /*HAVE_SETIMAGEOPTION*/
622 }
623
624 typedef struct _MagickColorspaceTypeNames {
625 ColorspaceType colorspace;
626 const char *name;
627 } MagickColorspaceTypeNames;
628
629 static MagickColorspaceTypeNames magick_colorspace_names[] = {
630 { UndefinedColorspace, "UndefinedColorspace" },
631 { CMYKColorspace, "CMYKColorspace" },
632 { GRAYColorspace, "GRAYColorspace" },
633 { HSLColorspace, "HSLColorspace" },
634 { HWBColorspace, "HWBColorspace" },
635 { OHTAColorspace, "OHTAColorspace" },
636 { Rec601YCbCrColorspace, "Rec601YCbCrColorspace" },
637 { Rec709YCbCrColorspace, "Rec709YCbCrColorspace" },
638 { RGBColorspace, "RGBColorspace" },
639 { sRGBColorspace, "sRGBColorspace" },
640 { TransparentColorspace, "TransparentColorspace" },
641 { XYZColorspace, "XYZColorspace" },
642 { YCbCrColorspace, "YCbCrColorspace" },
643 { YCCColorspace, "YCCColorspace" },
644 { YIQColorspace, "YIQColorspace" },
645 { YPbPrColorspace, "YPbPrColorspace" },
646 { YUVColorspace, "YUVColorspace" },
647
648 /* More recent imagemagicks add these.
649 */
650 #ifdef HAVE_CMYCOLORSPACE
651 { CMYColorspace, "CMYColorspace" },
652 { HCLColorspace, "HCLColorspace" },
653 { HSBColorspace, "HSBColorspace" },
654 { LabColorspace, "LabColorspace" },
655 { LogColorspace, "LogColorspace" },
656 { LuvColorspace, "LuvColorspace" },
657 #endif /*HAVE_CMYCOLORSPACE*/
658
659 #ifdef HAVE_HCLPCOLORSPACE
660 { HCLpColorspace, "HCLpColorspace" },
661 { HSIColorspace, "HSIColorspace" },
662 { HSVColorspace, "HSVColorspace" },
663 { LCHColorspace, "LCHColorspace" },
664 { LCHabColorspace, "LCHabColorspace" },
665 { LCHuvColorspace, "LCHuvColorspace" },
666 { LMSColorspace, "LMSColorspace" },
667 { scRGBColorspace, "scRGBColorspace" },
668 { xyYColorspace, "xyYColorspace" },
669 { YDbDrColorspace, "YDbDrColorspace" },
670 #endif /*HAVE_HCLPCOLORSPACE*/
671
672 /* im7 has this, I think
673 *
674 { LinearGRAYColorspace, "LinearGRAYColorspace" }
675 *
676 */
677 };
678
679 const char *
magick_ColorspaceType2str(ColorspaceType colorspace)680 magick_ColorspaceType2str( ColorspaceType colorspace )
681 {
682 int i;
683
684 for( i = 0; i < VIPS_NUMBER( magick_colorspace_names ); i++ )
685 if( magick_colorspace_names[i].colorspace == colorspace )
686 return( magick_colorspace_names[i].name );
687
688 return( "<unknown ColorspaceType>" );
689 }
690
691 void
magick_vips_error(const char * domain,ExceptionInfo * exception)692 magick_vips_error( const char *domain, ExceptionInfo *exception )
693 {
694 if( exception ) {
695 if( exception->reason &&
696 exception->description )
697 vips_error( domain, _( "libMagick error: %s %s" ),
698 exception->reason, exception->description );
699 else if( exception->reason )
700 vips_error( domain, _( "libMagick error: %s" ),
701 exception->reason );
702 else
703 vips_error( domain, "%s", _( "libMagick error:" ) );
704 }
705 }
706
707 static void *
magick_genesis_cb(void * client)708 magick_genesis_cb( void *client )
709 {
710 ExceptionInfo *exception;
711
712 #ifdef DEBUG
713 printf( "magick_genesis_cb:\n" );
714 #endif /*DEBUG*/
715
716 #if defined(HAVE_MAGICKCOREGENESIS) || defined(HAVE_MAGICK7)
717 MagickCoreGenesis( vips_get_argv0(), MagickFalse );
718 #else /*!HAVE_MAGICKCOREGENESIS*/
719 InitializeMagick( vips_get_argv0() );
720 #endif /*HAVE_MAGICKCOREGENESIS*/
721
722 /* This forces *magick to init all loaders. We have to do this so we
723 * can sniff files with GetImageMagick().
724 *
725 * We don't care about errors from magickinit.
726 */
727 exception = magick_acquire_exception();
728 (void) GetMagickInfo( "*", exception );
729 magick_destroy_exception(exception);
730
731 return( NULL );
732 }
733
734 void
magick_genesis(void)735 magick_genesis( void )
736 {
737 static GOnce once = G_ONCE_INIT;
738
739 VIPS_ONCE( &once, magick_genesis_cb, NULL );
740 }
741
742 /* Set vips metadata from a magick profile.
743 */
744 static void *
magick_set_vips_profile_cb(Image * image,const char * name,const void * data,size_t length,void * a)745 magick_set_vips_profile_cb( Image *image,
746 const char *name, const void *data, size_t length, void *a )
747 {
748 VipsImage *im = (VipsImage *) a;
749
750 char name_text[256];
751 VipsBuf vips_name = VIPS_BUF_STATIC( name_text );
752
753 if( strcasecmp( name, "XMP" ) == 0 )
754 vips_buf_appendf( &vips_name, VIPS_META_XMP_NAME );
755 else if( strcasecmp( name, "IPTC" ) == 0 )
756 vips_buf_appendf( &vips_name, VIPS_META_IPTC_NAME );
757 else if( strcasecmp( name, "ICC" ) == 0 )
758 vips_buf_appendf( &vips_name, VIPS_META_ICC_NAME );
759 else if( strcasecmp( name, "EXIF" ) == 0 )
760 vips_buf_appendf( &vips_name, VIPS_META_EXIF_NAME );
761 else
762 vips_buf_appendf( &vips_name, "magickprofile-%s", name );
763
764 vips_image_set_blob_copy( im,
765 vips_buf_all( &vips_name ), data, length );
766
767 if( strcmp( name, "exif" ) == 0 )
768 (void) vips__exif_parse( im );
769
770 return( NULL );
771 }
772
773 /* Set vips metadata from ImageMagick profiles.
774 */
775 int
magick_set_vips_profile(VipsImage * im,Image * image)776 magick_set_vips_profile( VipsImage *im, Image *image )
777 {
778 if( magick_profile_map( image, magick_set_vips_profile_cb, im ) )
779 return( -1 );
780
781 return( 0 );
782 }
783
784 typedef struct {
785 Image *image;
786 ExceptionInfo *exception;
787 } CopyProfileInfo;
788
789 static void *
magick_set_magick_profile_cb(VipsImage * im,const char * name,GValue * value,CopyProfileInfo * info)790 magick_set_magick_profile_cb( VipsImage *im,
791 const char *name, GValue *value, CopyProfileInfo *info )
792 {
793 char txt[256];
794 VipsBuf buf = VIPS_BUF_STATIC( txt );
795 const void *data;
796 size_t length;
797
798 if( strcmp( name, VIPS_META_XMP_NAME ) == 0 )
799 vips_buf_appendf( &buf, "XMP" );
800 else if( strcmp( name, VIPS_META_IPTC_NAME ) == 0 )
801 vips_buf_appendf( &buf, "IPTC" );
802 else if( strcmp( name, VIPS_META_ICC_NAME ) == 0 )
803 vips_buf_appendf( &buf, "ICC" );
804 else if( strcmp( name, VIPS_META_EXIF_NAME ) == 0 )
805 vips_buf_appendf( &buf, "EXIF" );
806 else if( vips_isprefix( "magickprofile-", name ) )
807 vips_buf_appendf( &buf,
808 "%s", name + strlen( "magickprofile-" ) );
809
810 if( vips_buf_is_empty( &buf ) )
811 return( NULL );
812 if( !vips_image_get_typeof( im, name ) )
813 return( NULL );
814 if( vips_image_get_blob( im, name, &data, &length ) )
815 return( im );
816
817 if( !magick_set_profile( info->image,
818 vips_buf_all( &buf ), data, length, info->exception ) )
819 return( im );
820
821 return( NULL );
822 }
823
824 /* Set magick metadata from a VipsImage.
825 */
826 int
magick_set_magick_profile(Image * image,VipsImage * im,ExceptionInfo * exception)827 magick_set_magick_profile( Image *image,
828 VipsImage *im, ExceptionInfo *exception )
829 {
830 CopyProfileInfo info;
831
832 info.image = image;
833 info.exception = exception;
834 if( vips_image_map( im,
835 (VipsImageMapFn) magick_set_magick_profile_cb, &info ) )
836 return( -1 );
837
838 return( 0 );
839 }
840
841 #endif /*defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7)*/
842