· 8 years ago · Aug 18, 2018, 09:04 PM
1/***********************************************************************/
2/* Open Visualization Data Explorer */
3/* (C) Copyright IBM Corp. 1989,1999 */
4/* ALL RIGHTS RESERVED */
5/* This code licensed under the */
6/* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */
7/***********************************************************************/
8/*
9* _IM_image.c : calls ImageMagick API to write files, especially useful for those files
10* whose formats DX does not support natively.
11* FIXED!: does not write intermediate miff file if the miff does not exist (i.e. is not to be appended).
12*FIXME: dx does not know all extensions that IM supports, can dx query IM API for the
13* supported extensions and thereby alleviate the tedious supplying of both format and extension?
14*/
15
16/*
17* This was coded to preserve dx's prior behavior for other extensions/formats, entailing some complexity.
18* How did we get here?
19*
20* Currently from WriteImage, there are two ways to get here:
21* (1) special cases e.g. gif where an entry in ImageTable(in _rw_image.c)
22* specifies the format and acceptable extensions and write_im is specified as the write function.
23* The special case is triggered by giving the format and/or a listed extension.
24* (2) "ImageMagick supported format" format was specified and or the extension was first matched in
25* the table entry for "ImageMagick supported format" format.
26* The extension must be included, recognized or not, in this case so ImageMagick knows what to write.
27* This scheme permits the user to specify, via an extention, an output format the dx coders did not know about
28* but that ImageMagick has added (perhaps later, perhaps as a delegate).
29* This scheme also allows the coexistence of DX methods and IM methods for writing image files. For example,
30* a format of null and an .rgb (or missing) extension gives the DX rgb output, while a format of
31* "ImageMagick supported format" and an .rgb extension gives the more conventional raw red/green/blue bytes in a file.
32* Another example is specifying "filename.miff" and "ImageMagick supported format", where dx writes its miff
33* and IM converts and overwrites it to its liking.
34*
35* DX prefers the "format" to the extension, IM goes by the extension.
36* WriteImage parameters can be (optional) format and (optional) filename+extension or filename .
37* Whether dot-anything is accepted as an extension depends on the listing in ImageTable (_rw_image.c).
38* dx will assume dx rgb format if the
39*format is null and the filename's extension is not recognized (i.e. we won't get to this function).
40*if dx recognized the extension for the format, the extension was
41*removed from the basename and now the ImageArgs.extension points to the extension.
42*however, dx will not (currently) have hardcoded all extensions that IM supports.
43*So it is easily possible that by the time we get here, we have a format of "ImageMagick supported format" or null
44* and a filename
45* with or without an extension appended, with or without a null imageargs.extension.
46*/
47/*
48* DX and IM name spaces collide on these terms (for IM 4.2.8 at least)
49*/
50#define ImageInfo DXImageInfo
51#define ImageType DXImageType
52
53#include <dxconfig.h>
54
55
56#include <stdio.h>
57#include <fcntl.h>
58#include <string.h>
59#include <dx/dx.h>
60#include "_helper_jea.h"
61#include "_rw_image.h"
62#include <sys/types.h>
63
64#if defined(HAVE_UNISTD_H)
65#include <unistd.h>
66#endif
67
68#if ( CAN_HAVE_ARRAY_DASD == 1 )
69#include <iop/mov.h>
70#include <iop/pfs.h>
71#endif
72
73#if defined(HAVE_SYS_STAT_H)
74#include <sys/stat.h>
75#endif
76
77#ifdef HAVE_LIBMAGICK
78/* we have some namespace conflicts with ImageMagick */
79#undef ImageInfo
80#undef ImageType
81#include <magick/api.h>
82#endif /* def HAVE_LIBMAGICK */
83
84#if (0)
85#define DEBUGMESSAGE(mess) DXMessage((mess))
86#else
87#define DEBUGMESSAGE(mess)
88#endif
89
90#define STREQ(s1,s2) (strcmp((s1),(s2))==0)
91
92static Error write_im(RWImageArgs *iargs);
93
94/*
95 * DXWrite out the given image in some format supported by ImageMagick
96 * currently through an intermediary file in miff (Magick Image File
97 * Format) though ideally this would use ImageMagick-4.2.8+'s blob support
98 * for in-memory translation.
99 */
100Error
101_dxf_write_im(RWImageArgs *iargs) {
102 /*
103 * how does one error check ? let IM flag the error for an unsupported image format
104 * if (iargs->imgtyp != img_typ_fb)
105 * DXErrorGoto(ERROR_INTERNAL, "_dxf_write_fb: mismatched image types");
106 */
107
108 return write_im(iargs);
109}
110
111static Error write_im(RWImageArgs *iargs) {
112#ifdef HAVE_LIBMAGICK
113 RWImageArgs tmpargs = *iargs;
114 Image *image=NULL;
115 Image *resize_image=NULL;
116 int err = 0;
117 int pixelsize;
118 int linesize;
119 Field field;
120 Array array;
121 int i;
122 int dim[3], ndim;
123 int nx, ny;
124 Type dxcolortype;
125 int imcolortype;
126 char *p1, *p2;
127 void* colors;
128 void* copycolors=NULL;
129 char *compressFlag = NULL;
130 CompressionType ctype = UndefinedCompression;
131
132
133 ExceptionInfo
134 _dxd_exception_info;
135 ImageInfo* image_info;
136 ImageInfo* new_frame_info;
137
138
139 int miff_exists_flag = 0;
140 char* miff_filename = NULL;
141 FILE* miff_fp;
142
143 DEBUGMESSAGE("requested extension is ");
144 /* if basename's .ext recognized as a format, it is removed from basename and
145 * iargs->extension points to it */
146 if(!iargs->extension) {
147 /* no extension or not recognized */
148 if(iargs->format) {
149 if(strcmp(iargs->format,"ImageMagick supported format") && strcmp(iargs->format, "ImageMagick")) {
150 char* firstspace;
151 /* not "ImageMagick supported format" format, use format for extension */
152 iargs->extension=iargs->format;
153 /* strip junk we can't deal withafter format... like "gif delayed=1" */
154 firstspace=strchr(iargs->extension, ' ');
155 if(firstspace != NULL)
156 *firstspace='\0';
157
158 }
159 }
160 }
161 /* still no recognized extension or "unique" format? it had better be on the filename! */
162 if(!iargs->extension) {
163 int i;
164 for(i=strlen(iargs->basename);i>0;--i) {
165 if(iargs->basename[i]=='.') {
166 iargs->extension=&((iargs->basename)[i+1]);
167 iargs->basename[i]='\0';
168 i=0;
169 } else if (iargs->basename[i]=='/')
170 i=0;
171 }
172 }
173 /*
174 * before proceeding,
175 * find out if we can handle the conversion to the requested extension
176 */
177
178 /*
179 * 8/99 some ambiguity here, since miff files are appendable.
180 * My arbitrary choice: if the miff file exists already, we append and do not erase.
181 * if the miff file does not exist, we erase when we are done.
182 * So if you want a multi-image file in some non-natively-supported-dx-format,
183 * writeimage to a miff, then on the last frame writeimage to e.g. an
184 * mpeg of the same name, specifying "im" format. Then it is up to you to erase it.
185 * Maybe some other scenario will prevail.
186 */
187
188 /* Only appending to miff files with extension .miff or type MIFF ignore this for others */
189
190 if(strcmp(iargs->extension, "miff") == 0) {
191 tmpargs.format="miff";
192 tmpargs.imgtyp=img_typ_miff;
193
194 /* does file exist? */
195 miff_filename = (char *)DXAllocateLocal(strlen(iargs->basename)+strlen(tmpargs.format)+2);
196 strcpy(miff_filename,iargs->basename);
197 strcat(miff_filename,".");
198 strcat(miff_filename,tmpargs.format);
199 if((miff_fp=fopen(miff_filename,"r"))) {
200 miff_exists_flag=1;
201 fclose(miff_fp);
202 } else {
203 miff_exists_flag=0;
204 }
205 if(miff_filename) DXFree((Pointer)miff_filename);
206 }
207
208
209 GetExceptionInfo(&_dxd_exception_info);
210 image_info=CloneImageInfo((ImageInfo *) NULL);
211
212
213 if(miff_exists_flag) {
214 FILE *existFile, *newFrameFile;
215 char *buf;
216 int noir;
217
218 new_frame_info=CloneImageInfo((ImageInfo *) NULL);
219
220 DEBUGMESSAGE("starting miff appending");
221 DEBUGMESSAGE(iargs->basename);
222 /* _dxf_write_miff(&tmpargs); */
223
224 /*
225 Initialize the image info structure and read an image.
226 */
227
228 (void) strcpy(image_info->filename,iargs->basename);
229 (void) strcat(image_info->filename,".");
230 (void) strcat(image_info->filename,tmpargs.format);
231
232 DEBUGMESSAGE("reading following file");
233 DEBUGMESSAGE(image_info->filename);
234
235 /* Now create new frame that will be added onto image. */
236
237 field=iargs->image;
238 array=(Array)DXGetComponentValue(field,"colors");
239 DXGetArrayInfo(array,NULL,&dxcolortype,NULL,NULL,NULL);
240 if (dxcolortype == TYPE_FLOAT) {
241 imcolortype = FloatPixel;
242 pixelsize = 3*sizeof(float);
243 } else {
244 imcolortype = CharPixel;
245 pixelsize = 3;
246 }
247
248 DXGetStringAttribute((Object)field, "compression", &compressFlag);
249 if(compressFlag == NULL)
250 ctype = UndefinedCompression;
251 else if(strcmp(compressFlag, "BZip")==0)
252 ctype = BZipCompression;
253 else if(strcmp(compressFlag, "Fax")==0)
254 ctype = FaxCompression;
255 else if(strcmp(compressFlag, "Group4")==0)
256 ctype = Group4Compression;
257 else if(strcmp(compressFlag, "JPEG")==0)
258 ctype = JPEGCompression;
259 else if(strcmp(compressFlag, "LZW")==0)
260 ctype = LZWCompression;
261 else if(strcmp(compressFlag, "RLE")==0)
262 ctype = RunlengthEncodedCompression;
263 else if(strcmp(compressFlag, "Zip")==0)
264 ctype = ZipCompression;
265
266 new_frame_info->compression = ctype;
267
268 colors=(void*)DXGetArrayData(array);
269 array=(Array)DXGetComponentValue(field,"connections");
270 DXQueryGridConnections(array,&ndim,dim);
271 nx=dim[1];
272 ny=dim[0];
273 copycolors = (void*) DXAllocate(nx*ny*pixelsize);
274 if(!copycolors) {
275 DestroyImageInfo(new_frame_info);
276 DestroyImageInfo(image_info);
277 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating copycolors _im_image.c");
278 }
279 linesize= nx*pixelsize;
280 p1=(char*)colors;
281 p2=(char*)copycolors;
282 p1+=(ny-1)*linesize;
283 for (i=0; i<ny; ++i) {
284 memcpy(p2,p1,linesize);
285 p1-=linesize;
286 p2+=linesize;
287 }
288
289 image=ConstituteImage(nx,ny,"RGB",imcolortype,(void*)copycolors,&_dxd_exception_info);
290 if (!image) {
291 DestroyImageInfo(new_frame_info);
292 DestroyImageInfo(image_info);
293 DXFree(copycolors);
294 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating Image _im_image.c");
295 }
296
297 image->compression = ctype;
298 new_frame_info->compression = ctype;
299 {
300 char gam[7];
301 sprintf(gam, "%2.4f", iargs->gamma);
302 GammaImage(image, gam);
303 }
304
305 TemporaryFilename(image->filename);
306 strcpy(new_frame_info->filename, image->filename);
307 DEBUGMESSAGE(new_frame_info->filename);
308
309 if(iargs->reduction > 0) {
310 int width = (int)(iargs->reduction/100.0 * nx);
311 int height = (int)(iargs->reduction/100.0 * ny);
312 resize_image = ResizeImage(image, width, height, LanczosFilter, 1.0, &_dxd_exception_info);
313 if (!image) {
314 DestroyImageInfo(image_info);
315 DXFree(copycolors);
316 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating Image for resize in _im_image.c");
317 } else {
318 DestroyImage(image);
319 image = resize_image;
320 }
321 }
322
323 /* Write to temp file */
324
325 err = WriteImage(new_frame_info, image);
326 if(err == 0) {
327 DXFree(copycolors);
328 DestroyImage(image);
329 DestroyImageInfo(new_frame_info);
330 DestroyImageInfo(image_info);
331#if MagickLibVersion > 0x0537
332 DestroyConstitute();
333#endif
334 DXSetError(ERROR_INTERNAL, "reason = %s, description = %s",
335 image->exception.reason,
336 image->exception.description);
337 }
338
339
340 /* Now append image */
341
342 buf = (char*) DXAllocate(4000);
343 if (!buf) {
344 DXFree(copycolors);
345 DestroyImage(image);
346 DestroyImageInfo(new_frame_info);
347 DestroyImageInfo(image_info);
348#if MagickLibVersion > 0x0537
349 DestroyConstitute();
350#endif
351 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating buffer _im_image.c");
352 }
353
354 existFile = fopen(image_info->filename, "ab");
355 newFrameFile = fopen(new_frame_info->filename, "rb");
356
357 while( !feof(newFrameFile) ) {
358 noir = fread(buf, 1, 4000, newFrameFile);
359 if(fwrite(buf, 1, noir, existFile) != noir) {
360 DXErrorReturn( ERROR_INTERNAL, "error writing file." );
361 }
362 }
363
364 fclose(existFile);
365 fclose(newFrameFile);
366
367 remove(new_frame_info->filename);
368
369 DEBUGMESSAGE("back from appending the image, what did I get?");
370
371 /* Now cleanup */
372 DXFree(buf);
373 DXFree(copycolors);
374 DestroyImage(image);
375 DestroyImageInfo(image_info);
376 DestroyImageInfo(new_frame_info);
377#if MagickLibVersion > 0x0537
378 DestroyConstitute();
379#endif
380
381 DEBUGMESSAGE("back from DestroyImage");
382
383 } else { /* miff does not exist, do this the new way by ConstituteImage */
384
385 field=iargs->image;
386 array=(Array)DXGetComponentValue(field,"colors");
387 DXGetArrayInfo(array,NULL,&dxcolortype,NULL,NULL,NULL);
388 if (dxcolortype == TYPE_FLOAT) {
389 imcolortype = FloatPixel;
390 pixelsize = 3*sizeof(float);
391 } else {
392 imcolortype = CharPixel;
393 pixelsize = 3;
394 }
395
396 ctype = UndefinedCompression;
397 if(iargs->compression) {
398 if(strcmp(iargs->compression, "BZip")==0)
399 ctype = BZipCompression;
400 else if(strcmp(iargs->compression, "Fax")==0)
401 ctype = FaxCompression;
402 else if(strcmp(iargs->compression, "Group4")==0)
403 ctype = Group4Compression;
404 else if(strcmp(iargs->compression, "JPEG")==0)
405 ctype = JPEGCompression;
406 else if(strcmp(iargs->compression, "LZW")==0)
407 ctype = LZWCompression;
408 else if(strcmp(iargs->compression, "RLE")==0)
409 ctype = RunlengthEncodedCompression;
410 else if(strcmp(iargs->compression, "Zip")==0)
411 ctype = ZipCompression;
412 }
413
414 if(iargs->quality > 0)
415 image_info->quality = iargs->quality;
416
417 colors=(void*)DXGetArrayData(array);
418 array=(Array)DXGetComponentValue(field,"connections");
419 DXQueryGridConnections(array,&ndim,dim);
420 nx=dim[1];
421 ny=dim[0];
422 copycolors = (void*) DXAllocate(nx*ny*pixelsize);
423 if(!copycolors) {
424 DestroyImageInfo(image_info);
425 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating copycolors _im_image.c");
426 }
427 linesize= nx*pixelsize;
428 p1=(char*)colors;
429 p2=(char*)copycolors;
430 p1+=(ny-1)*linesize;
431 for (i=0; i<ny; ++i) {
432 memcpy(p2,p1,linesize);
433 p1-=linesize;
434 p2+=linesize;
435 }
436
437 image=ConstituteImage(nx,ny,"RGB",imcolortype,(void*)copycolors,&_dxd_exception_info);
438 if (!image) {
439 DestroyImageInfo(image_info);
440 DXFree(copycolors);
441 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating Image _im_image.c");
442 }
443
444 /*
445 Write the image with ImageMagick
446 */
447 strcpy(image->filename,iargs->basename);
448 if(iargs->extension) {
449 strcat(image->filename,".");
450 strcat(image->filename,iargs->extension);
451 }
452
453 {
454 char gam[7];
455 sprintf(gam, "%2.4f", iargs->gamma);
456 GammaImage(image, gam);
457 }
458
459 /*
460 * Allow the user to use ImageMagick's resize filters to resize the image when
461 * writing. This gives us a cheap way to set up anti-aliasing.
462 */
463
464 if(iargs->reduction > 0) {
465 int width = (int)(iargs->reduction/100.0 * nx);
466 int height = (int)(iargs->reduction/100.0 * ny);
467 resize_image = ResizeImage(image, width, height, LanczosFilter, 1.0, &_dxd_exception_info);
468 if (!image) {
469 DestroyImageInfo(image_info);
470 DXFree(copycolors);
471 DXErrorReturn( ERROR_INTERNAL , "out of memory allocating Image for resize in _im_image.c");
472 } else {
473 DestroyImage(image);
474 image = resize_image;
475 }
476 }
477
478 image->compression = ctype;
479 image_info->compression = ctype;
480
481 DEBUGMESSAGE(image->filename);
482
483 err = WriteImage(image_info,image);
484 if(err == 0) {
485 DXSetError(ERROR_INTERNAL, "reason = %s, description = %s",
486 image->exception.reason,
487 image->exception.description);
488 }
489
490 DXFree(copycolors);
491 DestroyImage(image);
492 DestroyImageInfo(image_info);
493#if MagickLibVersion > 0x0537
494 DestroyConstitute();
495#endif
496 }
497 return (OK);
498#else /* ndef HAVE_LIBMAGICK */
499
500 DXErrorReturn(ERROR_NOT_IMPLEMENTED,"ImageMagick not included in build");
501#endif /* def HAVE_LIBMAGICK */
502
503}
504
505/*
506 * Search names, in order:
507 * ".<ext>", ".0.<ext>"
508 *
509 * set states saying whether:
510 * contains numeric
511 */
512char * _dxf_BuildIMReadFileName
513( char *buf,
514 int bufl,
515 char *basename, /* Has path but no extension */
516 char *fullname, /* As specified */
517 char *extension, /* File extension */
518 int framenum,
519 int *numeric ) /* Numbers postfixed onto name or not? */
520{
521 int i;
522 int fd = -1;
523 int appendable_files = 0;
524 char framestr[16];
525
526 DXASSERTGOTO ( NULL != buf );
527 DXASSERTGOTO ( 0 < bufl );
528 DXASSERTGOTO ( NULL != basename );
529 DXASSERTGOTO ( NULL != fullname );
530 DXASSERTGOTO ( NULL != extension );
531 DXASSERTGOTO ( 0 <= framenum );
532 DXASSERTGOTO ( NULL != numeric );
533
534 for ( i = 0; i < 2; i++ ) {
535 *numeric = i;
536
537 if ( *numeric && ( framenum == VALUE_UNSPECIFIED ) )
538 continue;
539
540 if (*numeric && !appendable_files)
541 sprintf(framestr,".%d",framenum);
542 else
543 framestr[0] = '\0';
544
545 /* Make sure buffer is big enough */
546 if ((strlen(basename) + strlen(framestr) + strlen(extension) + 2) > bufl)
547 DXErrorReturn(ERROR_INTERNAL,"Insufficient storage space for filename");
548
549 sprintf( buf, "%s%s.%s", basename, framestr, extension );
550
551 if ( 0 <= ( fd = open ( buf, O_RDONLY ) ) )
552 break;
553 }
554
555 if ( 0 > fd ) {
556 *numeric = 0;
557 strcpy ( buf, basename );
558 fd = open ( buf, O_RDONLY );
559 }
560
561 DXDebug( "R",
562 "_dxf_BuildIMReadFileName: File = %s, numeric = %d",
563 buf, *numeric );
564
565 if (fd > -1)
566 close ( fd );
567
568 return buf;
569
570}
571
572/*
573 * Determine the number of images in an ImageMagick file.
574 */
575#if defined(HAVE_LIBMAGICK)
576
577static int _dxf_GetNumImagesInFile( ImageInfo *image_info ) {
578 /* FIXME: Currently, ImageMagick has no way to determine the number of
579 * images in a (potentially multi-image) image file without reading
580 * the data for all of the images into memory at once. This was
581 * confirmed by folks on the ImageMagick mailing list.
582 *
583 * Primary evidence of this is that some readers (e.g. PNG) completely
584 * ignore the ImageInfo subimage and subrange fields, and Magick does
585 * not support reading images (all or a subset) in ping=1 mode.
586 * So subimage specification and multi-image ping is pointless.
587 *
588 * We have a choice here between:
589 * 1) general, typeless image handling, where we take a performance hit
590 * to read all images in a multi-image sequence into VM (possibly
591 * exhausting VM) to access all, one, or some subset in DX
592 * 2) general, typeless image handling, where we limit access to only
593 * one sub-image
594 * 3) special cases based on image type
595 *
596 * Because we support separate-file multi-image read via IM, and
597 * DX's native TIFF and MIFF readers support single-file multi-image, we
598 * choose option #2. This routine should be fixed if/when ImageMagick
599 * adds better support for single-file multi-image reads.
600 */
601
602 return 1;
603}
604
605#endif
606
607
608/*
609 * Set a SizeData struct with values.
610 * set state saying whether:
611 * has multiple images in file
612 * Careful:
613 * if the images are stored internally,
614 * then reset the use_numerics flag.
615 */
616SizeData * _dxf_ReadImageSizesIM
617( char *name,
618 int startframe,
619 SizeData *data,
620 int *use_numerics,
621 int *multiples ) {
622#if !defined(HAVE_LIBMAGICK)
623 DXErrorReturn(ERROR_NOT_IMPLEMENTED,"ImageMagick 5 not included in build");
624#else
625
626 Image *image;
627 ImageInfo *image_info = NULL;
628 ExceptionInfo _dxd_exception_info;
629 char user_basename [ MAX_IMAGE_NAMELEN ];
630 char newname[ MAX_IMAGE_NAMELEN ];
631 char extension[40];
632 int fh;
633
634 DXASSERTGOTO ( ERROR != name );
635 DXASSERTGOTO ( ERROR != data );
636 DXASSERTGOTO ( ERROR != use_numerics );
637 DXASSERTGOTO ( ERROR != multiples );
638
639 /* Magick init */
640 GetExceptionInfo( &_dxd_exception_info );
641 image_info = CloneImageInfo( NULL );
642
643 /* PingImage returns info on the first image (possibly in a sequence) */
644 image_info->filename[0] = '\0';
645 strncat( image_info->filename, name, sizeof( image_info->filename ) - 1 );
646
647 if ( (image = PingImage( image_info, &_dxd_exception_info )) == NULL )
648 DXErrorGoto2( ERROR_BAD_PARAMETER, "#13950",
649 /* failed to read image in file '%s' */ name );
650
651 data->height = image->rows;
652 data->width = image->columns;
653
654 data->startframe = ( startframe == VALUE_UNSPECIFIED ) ? 0 : startframe;
655 data->endframe = ( data->startframe +
656 _dxf_GetNumImagesInFile( image_info ) - 1 );
657 *multiples = data->startframe != data->endframe;
658 if ( *multiples )
659 *use_numerics = 0;
660
661 /*
662 * Intent: If use_numerics (implies there aren't multiple images
663 * in this file), see if there are multiple numbered files on disk.
664 */
665 if ( !(*use_numerics) )
666 *multiples = 0;
667 else {
668 /* Magick filenames "always" have a filetype extension */
669 user_basename[0] = extension[0] = '\0';
670 _dxf_ExtractImageExtension(name,img_typ_im,
671 extension,sizeof(extension));
672 strncat( user_basename, name, sizeof(user_basename)-1 );
673 _dxf_RemoveImageExtension( user_basename, img_typ_im );
674 _dxf_RemoveExtension( user_basename );
675
676 do {
677 sprintf( newname, "%s.%d.%s",
678 user_basename, data->endframe, extension );
679 if ( (fh = open ( newname, O_RDONLY )) < 0 )
680 break;
681
682 close ( fh );
683 data->endframe++;
684 DXDebug ( "R", "_dxf_ReadImageSizesIM: opened: %s", newname );
685 } while ( 1 );
686
687 data->endframe--;
688 }
689
690 DXDebug ( "R",
691 "_dxf_ReadImageSizesIM: h,w, s,e = %d,%d, %d,%d; "
692 "numer = %d, multi = %d",
693 data->height, data->width, data->startframe, data->endframe,
694 *use_numerics, *multiples );
695
696 if ( image_info )
697 DestroyImageInfo( image_info );
698 if ( image )
699 DestroyImage( image );
700 return data;
701
702error:
703 if ( image_info )
704 DestroyImageInfo( image_info );
705 if ( image )
706 DestroyImage( image );
707 return NULL;
708
709#endif /* HAVE_LIBMAGICK */
710}
711
712/*
713 * Read an image from a file into a DX image field using ImageMagick.
714 */
715Field _dxf_InputIM( int width, int height, char *name, int relframe,
716 int delayed, char *colortype ) {
717#if !defined(HAVE_LIBMAGICK)
718 DXErrorReturn(ERROR_NOT_IMPLEMENTED,"ImageMagick 5 not included in build");
719#else
720
721 Field dx_image = NULL;
722 Image *image = NULL,
723 *image_tmp;
724 ImageInfo *image_info = NULL;
725 ImageType image_type;
726 ExceptionInfo _dxd_exception_info;
727 Type type;
728 int rank, shape[32];
729 Array colorsArray = NULL;
730 Array opacitiesArray = NULL;
731 Pointer pixels;
732 Pointer opacities = NULL;
733 StorageType storage_type;
734 int i;
735
736 DXDebug ( "R", "_dxf_InputIM: name = %s, relframe = %d", name, relframe );
737
738 if ((width * height) == 0)
739 DXErrorGoto2( ERROR_BAD_PARAMETER, "#12275", 0 );
740
741 /* Magick init */
742 GetExceptionInfo( &_dxd_exception_info );
743 image_info = CloneImageInfo( NULL );
744
745 image_info->filename[0] = '\0';
746 strncat( image_info->filename, name, sizeof( image_info->filename ) - 1 );
747
748 image_info->subimage=relframe;
749 image_info->subrange=1;
750
751 if ( (image = ReadImage( image_info, &_dxd_exception_info )) == NULL )
752 DXErrorGoto2( ERROR_BAD_PARAMETER, "#13950",
753 /* failed to read image in file '%s' */ name );
754
755 if ( width != image->columns || height != image->rows )
756 DXErrorGoto(ERROR_INTERNAL,
757 "Image dimensions don't match requested dimensions" );
758
759 /* If it's CMYK, convert to RGB. */
760 if ( image->colorspace == CMYKColorspace )
761 if ( !TransformRGBImage( image, RGBColorspace ) )
762 DXErrorGoto(ERROR_INTERNAL, "CMYK->RGB transform failed" );
763
764 /*
765 * Set the colors (and possibly color map) components
766 */
767#if MagickLibVersion < 0x0540
768
769 image_type = GetImageType( image );
770#else
771
772 image_type = GetImageType( image, &_dxd_exception_info );
773#endif
774
775 switch ( image_type ) {
776 default :
777
778 /* Since we handled CMYK above, we should never get here */
779 DXErrorGoto2( ERROR_DATA_INVALID, "#13960", image_type );
780
781 /**********************************************************************/
782 /* OPTION #1: Source is RGB direct (dest is float or byte direct) */
783 case TrueColorType :
784 case TrueColorMatteType :
785
786 /* Create the image field */
787 dx_image = DXMakeImageFormat(width, height, colortype);
788 if (! dx_image)
789 goto error;
790
791 colorsArray = (Array)DXGetComponentValue(dx_image, "colors");
792 DXGetArrayInfo(colorsArray, NULL, &type, NULL, &rank, shape);
793
794 /* Colors are 3-vectors (direct) */
795 if (rank != 1 || shape[0] != 3 ||
796 (type != TYPE_UBYTE && type != TYPE_FLOAT))
797 DXErrorGoto(ERROR_INTERNAL,
798 "Invalid image field direct color format" );
799
800 storage_type = ( type == TYPE_UBYTE ) ? CharPixel : FloatPixel;
801 pixels = DXGetArrayData(colorsArray);
802
803 /* Transfer the pixels to the field */
804 /* (NOTE: DX stores rows bottom-to-top, so flip first) */
805 image_tmp = FlipImage( image, &_dxd_exception_info );
806 if ( !image_tmp )
807 DXErrorGoto(ERROR_INTERNAL, "Failed to flip image" );
808 DestroyImage( image );
809 image = image_tmp;
810
811#if MagickLibVersion < 0x0540
812 DispatchImage( image, 0, 0, width, height, "RGB", storage_type,
813 pixels );
814#else
815 DispatchImage( image, 0, 0, width, height, "RGB", storage_type,
816 pixels, &_dxd_exception_info);
817#endif
818 /* Handle transparency (opacities are floats) */
819 if ( image->matte ) {
820 float *optr;
821
822 if ( !(opacitiesArray = DXNewArray( TYPE_FLOAT,
823 CATEGORY_REAL, 0 )) ||
824 !DXAddArrayData( opacitiesArray, 0, width*height, NULL) ||
825 !DXSetComponentValue( (Field)dx_image, "opacities",
826 (Object)opacitiesArray ) ||
827 !(opacities = DXGetArrayData(opacitiesArray)) ||
828 !DXEndField( (Field)dx_image ) )
829 DXErrorGoto(ERROR_INTERNAL, "Failed to create opacities" );
830
831#if MagickLibVersion < 0x0540
832 DispatchImage( image, 0, 0, width, height, "A", FloatPixel,
833 opacities );
834#else
835 DispatchImage( image, 0, 0, width, height, "A", FloatPixel,
836 opacities, &_dxd_exception_info );
837#endif
838
839 for ( i = width * height, optr = (float *)opacities;
840 i > 0; i--, optr++ )
841 *optr = 1.0 - *optr;
842 }
843 break;
844
845 /**********************************************************************/
846 /* OPTION #2: Source is palettized (includes gray scale or B&W) */
847 /* (dest is float or byte direct, or byte delayed with a */
848 /* 256-element float colormap) */
849 case PaletteType :
850 case PaletteMatteType :
851 case GrayscaleType :
852 case GrayscaleMatteType :
853 case BilevelType :
854
855 /* ReadImage(delayed=<y/n>) only applies to delayed color images */
856 if (delayed == DELAYED_YES)
857 colortype = COLORTYPE_DELAYED;
858
859 /* Create the image field */
860 dx_image = DXMakeImageFormat(width, height, colortype);
861 if (! dx_image)
862 goto error;
863
864 colorsArray = (Array)DXGetComponentValue(dx_image, "colors");
865 DXGetArrayInfo(colorsArray, NULL, &type, NULL, &rank, shape);
866 pixels = DXGetArrayData(colorsArray);
867
868 /* Colors are 3-vectors (direct) */
869 if (rank == 1 && shape[0] == 3) {
870 if (type != TYPE_UBYTE && type != TYPE_FLOAT)
871 DXErrorGoto(ERROR_INTERNAL,
872 "Invalid image field direct color format" );
873
874 storage_type = ( type == TYPE_UBYTE ) ? CharPixel : FloatPixel;
875
876 /* Transfer the pixels to the field */
877 /* (NOTE: DX stores rows bottom-to-top, so flip first) */
878 image_tmp = FlipImage( image, &_dxd_exception_info );
879 if ( !image_tmp )
880 DXErrorGoto(ERROR_INTERNAL,
881 "Failed to flip image" );
882 DestroyImage( image );
883 image = image_tmp;
884#if MagickLibVersion < 0x0540
885 DispatchImage( image, 0, 0, width, height, "RGB", storage_type,
886 pixels );
887#else
888 DispatchImage( image, 0, 0, width, height, "RGB", storage_type,
889 pixels, &_dxd_exception_info );
890#endif
891 /* Handle transparency (opacities are floats) */
892 if ( image->matte ) {
893 float *optr;
894
895 if ( !(opacitiesArray = DXNewArray( TYPE_FLOAT,
896 CATEGORY_REAL, 0 )) ||
897 !DXAddArrayData( opacitiesArray, 0, width*height, NULL) ||
898 !DXSetComponentValue( (Field)dx_image, "opacities",
899 (Object)opacitiesArray ) ||
900 !(opacities = DXGetArrayData(opacitiesArray)) ||
901 !DXEndField( (Field)dx_image ) )
902 DXErrorGoto(ERROR_INTERNAL, "Failed to create opacities" );
903
904#if MagickLibVersion < 0x0540
905 DispatchImage( image, 0, 0, width, height, "A", FloatPixel,
906 opacities );
907#else
908 DispatchImage( image, 0, 0, width, height, "A", FloatPixel,
909 opacities, &_dxd_exception_info );
910#endif
911
912 for ( i = width * height, optr = (float *)opacities;
913 i > 0; i--, optr++ )
914 *optr = 1.0 - *optr;
915 }
916 }
917
918 /* Colors are scalars (delayed) */
919 else if (rank == 0 || (rank == 1 && shape[0] == 1)) {
920 Type mType;
921 int mRank, mShape[32], mLen;
922 int i,x,y;
923 float (*cmap)[3], *omap = NULL;
924 Array colorMap, opacityMap;
925
926 colorMap = (Array)DXGetComponentValue(dx_image, "color map");
927 if (! colorMap)
928 DXErrorGoto(ERROR_INTERNAL,
929 "single-valued image requires a color map" );
930
931 DXGetArrayInfo(colorMap, &mLen, &mType, NULL, &mRank, mShape);
932
933 if (mLen != 256 || mType != TYPE_FLOAT ||
934 mRank != 1 || mShape[0] != 3)
935 DXErrorGoto(ERROR_INTERNAL,
936 "DXMakeImageFormat returned invalid delayed-color image");
937
938 cmap = (float (*)[3]) DXGetArrayData(colorMap);
939
940 /* If transparency, opacities is byte & opacity map is float */
941 if ( image->matte ) {
942 if ( !(opacitiesArray = DXNewArray( TYPE_UBYTE,
943 CATEGORY_REAL, 0 )) ||
944 !DXAddArrayData( opacitiesArray, 0, width*height, NULL) ||
945 !DXSetComponentValue( (Field)dx_image, "opacities",
946 (Object)opacitiesArray ) ||
947 !(opacities = DXGetArrayData(opacitiesArray)) )
948 DXErrorGoto(ERROR_INTERNAL, "Failed to create opacities" );
949
950 if ( !(opacityMap = DXNewArray( TYPE_FLOAT,
951 CATEGORY_REAL, 0 )) ||
952 !DXAddArrayData( opacityMap, 0, 256, NULL) ||
953 !DXSetComponentValue( (Field)dx_image, "opacity map",
954 (Object)opacityMap ) ||
955 !(omap = DXGetArrayData(opacityMap)) )
956 DXErrorGoto(ERROR_INTERNAL, "Failed to create opacity map");
957
958 if ( !DXEndField( (Field)dx_image ) )
959 DXErrorGoto(ERROR_INTERNAL, "Failed to close field");
960 }
961
962 /* Read the colormap */
963 for ( i = 0; i < image->colors; i++ ) {
964 cmap[i][0] = ((float) image->colormap[i].red ) / MaxRGB;
965 cmap[i][1] = ((float) image->colormap[i].green ) / MaxRGB;
966 cmap[i][2] = ((float) image->colormap[i].blue ) / MaxRGB;
967 if ( image->matte )
968 omap[i] = (1.0 -
969 ((float) image->colormap[i].opacity ) / MaxRGB);
970 }
971 for ( ; i < 256; i++ ) {
972 cmap[i][0] = cmap[i][1] = cmap[i][2] = 0.0;
973 if ( image->matte )
974 omap[i] = 0.0;
975 }
976
977 /* Now copy the pixels (i.e. color/opacity map indicies) */
978 for ( y = 0; y < height; y++ ) {
979 ubyte *pptr = ((ubyte *)pixels ) + (height-1-y)*width;
980 ubyte *optr = NULL;
981 PixelPacket *pixies;
982 IndexPacket *indexes, *indexes2;
983
984 if ( image->matte )
985 optr = ((ubyte *)opacities) + (height-1-y)*width;
986
987 pixies = GetImagePixels( image, 0, y, width, 1 );
988 indexes = indexes2 = GetIndexes( image );
989
990 if ( sizeof(indexes[0]) == 1 )
991 memcpy( pptr, indexes, width );
992 else
993 for ( x = 0; x < width; x++ )
994 if( image->matte )
995 *(pptr++) = *(optr++) = *(indexes++);
996 else
997 *(pptr++) = *(indexes++);
998
999 /* Opacities in colormap is wrong; use direct color map */
1000 if ( image->matte )
1001 for ( x = 0; x < width; x++ )
1002 {
1003 int omap_index = *(indexes2++); // if Magick has HDRI, Quantum is a float
1004 omap[(omap_index < 0 ? 0 : omap_index)] = ( 1.0 - // clamp if negative
1005 ((float) (pixies++)->opacity) / MaxRGB );
1006 }
1007 }
1008 } else
1009 DXErrorGoto( ERROR_INTERNAL, "unexpected image field format" );
1010
1011 break;
1012 }
1013
1014 if ( image_info )
1015 DestroyImageInfo( image_info );
1016 if ( image )
1017 DestroyImage( image );
1018 return dx_image;
1019
1020error:
1021 if ( image_info )
1022 DestroyImageInfo( image_info );
1023 if ( image )
1024 DestroyImage( image );
1025 return NULL;
1026
1027#endif /* HAVE_LIBMAGICK */
1028}
1029
1030int /* 0/1 on failure/success */
1031_dxf_ValidImageExtensionIM(char *ext) {
1032#if !defined(HAVE_LIBMAGICK)
1033 DXErrorReturn(ERROR_NOT_IMPLEMENTED,"ImageMagick 5 not included in build");
1034#else
1035
1036 ExceptionInfo _dxd_exception_info;
1037 const MagickInfo *minfo;
1038
1039 GetExceptionInfo( &_dxd_exception_info );
1040 minfo = GetMagickInfo( ext, &_dxd_exception_info );
1041 return (minfo != NULL);
1042#endif
1043}