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