]> granicus.if.org Git - imagemagick/blob - coders/fpx.c
(no commit message)
[imagemagick] / coders / fpx.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            FFFFF  PPPP   X   X                              %
7 %                            F      P   P   X X                               %
8 %                            FFF    PPPP     X                                %
9 %                            F      P       X X                               %
10 %                            F      P      X   X                              %
11 %                                                                             %
12 %                                                                             %
13 %                     Read/Write FlashPIX Image Format                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/property.h"
44 #include "magick/blob.h"
45 #include "magick/blob-private.h"
46 #include "magick/cache.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/constitute.h"
51 #include "magick/exception.h"
52 #include "magick/exception-private.h"
53 #include "magick/geometry.h"
54 #include "magick/image.h"
55 #include "magick/image-private.h"
56 #include "magick/list.h"
57 #include "magick/magick.h"
58 #include "magick/memory_.h"
59 #include "magick/monitor.h"
60 #include "magick/monitor-private.h"
61 #include "magick/pixel.h"
62 #include "magick/quantum-private.h"
63 #include "magick/static.h"
64 #include "magick/string_.h"
65 #include "magick/module.h"
66 #if defined(MAGICKCORE_FPX_DELEGATE)
67 #if !defined(vms) && !defined(macintosh) && !defined(MAGICKCORE_WINDOWS_SUPPORT)
68 #include <fpxlib.h>
69 #else
70 #include "Fpxlib.h"
71 #endif
72 #endif
73 \f
74 #if defined(MAGICKCORE_FPX_DELEGATE)
75 /*
76   Forward declarations.
77 */
78 static MagickBooleanType
79   WriteFPXImage(const ImageInfo *,Image *);
80 #endif
81 \f
82 /*
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 %                                                                             %
85 %                                                                             %
86 %                                                                             %
87 %   I s F P X                                                                 %
88 %                                                                             %
89 %                                                                             %
90 %                                                                             %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %
93 %  IsFPX() returns MagickTrue if the image format type, identified by the
94 %  magick string, is FPX.
95 %
96 %  The format of the IsFPX method is:
97 %
98 %      MagickBooleanType IsFPX(const unsigned char *magick,const size_t length)
99 %
100 %  A description of each parameter follows:
101 %
102 %    o magick: compare image format pattern against these bytes.
103 %
104 %    o length: Specifies the length of the magick string.
105 %
106 */
107 static MagickBooleanType IsFPX(const unsigned char *magick,const size_t length)
108 {
109   if (length < 4)
110     return(MagickFalse);
111   if (memcmp(magick,"\320\317\021\340",4) == 0)
112     return(MagickTrue);
113   return(MagickFalse);
114 }
115 \f
116 #if defined(MAGICKCORE_FPX_DELEGATE)
117 /*
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 %                                                                             %
120 %                                                                             %
121 %                                                                             %
122 %   R e a d F P X I m a g e                                                   %
123 %                                                                             %
124 %                                                                             %
125 %                                                                             %
126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 %
128 %  ReadFPXImage() reads a FlashPix image file and returns it.  It
129 %  allocates the memory necessary for the new Image structure and returns a
130 %  pointer to the new image.  This method was contributed by BillR@corbis.com.
131 %
132 %  The format of the ReadFPXImage method is:
133 %
134 %      Image *ReadFPXImage(const ImageInfo *image_info,ExceptionInfo *exception)
135 %
136 %  A description of each parameter follows:
137 %
138 %    o image_info: the image info.
139 %
140 %    o exception: return any errors or warnings in this structure.
141 %
142 */
143 static Image *ReadFPXImage(const ImageInfo *image_info,ExceptionInfo *exception)
144 {
145   FPXColorspace
146     colorspace;
147
148   FPXImageComponentDesc
149     *alpha_component,
150     *blue_component,
151     *green_component,
152     *red_component;
153
154   FPXImageDesc
155     fpx_info;
156
157   FPXImageHandle
158     *flashpix;
159
160   FPXStatus
161     fpx_status;
162
163   FPXSummaryInformation
164     summary_info;
165
166   Image
167     *image;
168
169   IndexPacket
170     index;
171
172   ssize_t
173     y;
174
175   MagickBooleanType
176     status;
177
178   register IndexPacket
179     *indexes;
180
181   register ssize_t
182     i,
183     x;
184
185   register PixelPacket
186     *q;
187
188   register unsigned char
189     *a,
190     *b,
191     *g,
192     *r;
193
194   size_t
195     memory_limit;
196
197   unsigned char
198     *pixels;
199
200   unsigned int
201     height,
202     tile_width,
203     tile_height,
204     width;
205
206   size_t
207     scene;
208
209   /*
210     Open image.
211   */
212   assert(image_info != (const ImageInfo *) NULL);
213   assert(image_info->signature == MagickSignature);
214   if (image_info->debug != MagickFalse)
215     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
216       image_info->filename);
217   assert(exception != (ExceptionInfo *) NULL);
218   assert(exception->signature == MagickSignature);
219   image=AcquireImage(image_info);
220   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
221   if (status == MagickFalse)
222     {
223       image=DestroyImageList(image);
224       return((Image *) NULL);
225     }
226   (void) CloseBlob(image);
227   /*
228     Initialize FPX toolkit.
229   */
230   fpx_status=FPX_InitSystem();
231   if (fpx_status != FPX_OK)
232     ThrowReaderException(CoderError,"UnableToInitializeFPXLibrary");
233   memory_limit=20000000;
234   fpx_status=FPX_SetToolkitMemoryLimit(&memory_limit);
235   if (fpx_status != FPX_OK)
236     {
237       FPX_ClearSystem();
238       ThrowReaderException(CoderError,"UnableToInitializeFPXLibrary");
239     }
240   tile_width=64;
241   tile_height=64;
242   flashpix=(FPXImageHandle *) NULL;
243   {
244 #if defined(macintosh)
245     FSSpec
246       fsspec;
247
248     FilenameToFSSpec(image->filename,&fsspec);
249     fpx_status=FPX_OpenImageByFilename((const FSSpec &) fsspec,(char *) NULL,
250 #else
251     fpx_status=FPX_OpenImageByFilename(image->filename,(char *) NULL,
252 #endif
253       &width,&height,&tile_width,&tile_height,&colorspace,&flashpix);
254   }
255   if (fpx_status == FPX_LOW_MEMORY_ERROR)
256     {
257       FPX_ClearSystem();
258       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
259     }
260   if (fpx_status != FPX_OK)
261     {
262       FPX_ClearSystem();
263       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
264         image->filename);
265       image=DestroyImageList(image);
266       return((Image *) NULL);
267     }
268   if (colorspace.numberOfComponents == 0)
269     {
270       FPX_ClearSystem();
271       ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
272     }
273   if (image_info->view == (char *) NULL)
274     {
275       float
276         aspect_ratio;
277
278       /*
279         Get the aspect ratio.
280       */
281       aspect_ratio=(float) width/height;
282       fpx_status=FPX_GetImageResultAspectRatio(flashpix,&aspect_ratio);
283       if (fpx_status != FPX_OK)
284         ThrowReaderException(DelegateError,"UnableToReadAspectRatio");
285       if (width != (size_t) floor((aspect_ratio*height)+0.5))
286         Swap(width,height);
287     }
288   fpx_status=FPX_GetSummaryInformation(flashpix,&summary_info);
289   if (fpx_status != FPX_OK)
290     {
291       FPX_ClearSystem();
292       ThrowReaderException(DelegateError,"UnableToReadSummaryInfo");
293     }
294   if (summary_info.title_valid)
295     if ((summary_info.title.length != 0) &&
296         (summary_info.title.ptr != (unsigned char *) NULL))
297       {
298         char
299           *label;
300
301         /*
302           Note image label.
303         */
304         label=(char *) NULL;
305         if (~summary_info.title.length >= MaxTextExtent)
306           label=(char *) AcquireQuantumMemory(summary_info.title.length+
307             MaxTextExtent,sizeof(*label));
308         if (label == (char *) NULL)
309           {
310             FPX_ClearSystem();
311             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
312           }
313         (void) CopyMagickString(label,(char *) summary_info.title.ptr,
314           summary_info.title.length+1);
315         (void) SetImageProperty(image,"label",label);
316         label=DestroyString(label);
317       }
318   if (summary_info.comments_valid)
319     if ((summary_info.comments.length != 0) &&
320         (summary_info.comments.ptr != (unsigned char *) NULL))
321       {
322         char
323           *comments;
324
325         /*
326           Note image comment.
327         */
328         comments=(char *) NULL;
329         if (~summary_info.comments.length >= MaxTextExtent)
330           comments=(char *) AcquireQuantumMemory(summary_info.comments.length+
331             MaxTextExtent,sizeof(*comments));
332         if (comments == (char *) NULL)
333           {
334             FPX_ClearSystem();
335             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
336           }
337         (void) CopyMagickString(comments,(char *) summary_info.comments.ptr,
338           summary_info.comments.length+1);
339         (void) SetImageProperty(image,"comment",comments);
340         comments=DestroyString(comments);
341       }
342   /*
343     Determine resolution by scene specification.
344   */
345   for (i=1; ; i++)
346     if (((width >> i) < tile_width) || ((height >> i) < tile_height))
347       break;
348   scene=i;
349   if (image_info->number_scenes != 0)
350     while (scene > image_info->scene)
351     {
352       width>>=1;
353       height>>=1;
354       scene--;
355     }
356   if (image_info->size != (char *) NULL)
357     while ((width > image->columns) || (height > image->rows))
358     {
359       width>>=1;
360       height>>=1;
361       scene--;
362     }
363   image->depth=8;
364   image->columns=width;
365   image->rows=height;
366   if ((colorspace.numberOfComponents % 2) == 0)
367     image->matte=MagickTrue;
368   if (colorspace.numberOfComponents == 1)
369     {
370       /*
371         Create linear colormap.
372       */
373       if (AcquireImageColormap(image,MaxColormapSize) == MagickFalse)
374         {
375           FPX_ClearSystem();
376           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
377         }
378     }
379   if (image_info->ping != MagickFalse)
380     {
381       (void) FPX_CloseImage(flashpix);
382       FPX_ClearSystem();
383       return(GetFirstImageInList(image));
384     }
385   /*
386     Allocate memory for the image and pixel buffer.
387   */
388   pixels=(unsigned char *) AcquireQuantumMemory(image->columns,(tile_height+
389     1UL)*colorspace.numberOfComponents*sizeof(*pixels));
390   if (pixels == (unsigned char *) NULL)
391     {
392       FPX_ClearSystem();
393       (void) FPX_CloseImage(flashpix);
394       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
395     }
396   /*
397     Initialize FlashPix image description.
398   */
399   fpx_info.numberOfComponents=colorspace.numberOfComponents;
400   for (i=0; i < 4; i++)
401   {
402     fpx_info.components[i].myColorType.myDataType=DATA_TYPE_UNSIGNED_BYTE;
403     fpx_info.components[i].horzSubSampFactor=1;
404     fpx_info.components[i].vertSubSampFactor=1;
405     fpx_info.components[i].columnStride=fpx_info.numberOfComponents;
406     fpx_info.components[i].lineStride=image->columns*
407       fpx_info.components[i].columnStride;
408     fpx_info.components[i].theData=pixels+i;
409   }
410   fpx_info.components[0].myColorType.myColor=fpx_info.numberOfComponents > 2 ?
411     NIFRGB_R : MONOCHROME;
412   red_component=(&fpx_info.components[0]);
413   fpx_info.components[1].myColorType.myColor=fpx_info.numberOfComponents > 2 ?
414     NIFRGB_G : ALPHA;
415   green_component=(&fpx_info.components[1]);
416   fpx_info.components[2].myColorType.myColor=NIFRGB_B;
417   blue_component=(&fpx_info.components[2]);
418   fpx_info.components[3].myColorType.myColor=ALPHA;
419   alpha_component=(&fpx_info.components[fpx_info.numberOfComponents-1]);
420   FPX_SetResampleMethod(FPX_LINEAR_INTERPOLATION);
421   /*
422     Initialize image pixels.
423   */
424   for (y=0; y < (ssize_t) image->rows; y++)
425   {
426     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
427     if (q == (PixelPacket *) NULL)
428       break;
429     indexes=GetAuthenticIndexQueue(image);
430     if ((y % tile_height) == 0)
431       {
432         /*
433           Read FPX image tile (with or without viewing affine)..
434         */
435         if (image_info->view != (char *) NULL)
436           fpx_status=FPX_ReadImageRectangle(flashpix,0,y,image->columns,y+
437             tile_height-1,scene,&fpx_info);
438         else
439           fpx_status=FPX_ReadImageTransformRectangle(flashpix,0.0F,
440             (float) y/image->rows,(float) image->columns/image->rows,
441             (float) (y+tile_height-1)/image->rows,(ssize_t) image->columns,
442             (ssize_t) tile_height,&fpx_info);
443         if (fpx_status == FPX_LOW_MEMORY_ERROR)
444           {
445             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
446             (void) FPX_CloseImage(flashpix);
447             FPX_ClearSystem();
448             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
449           }
450       }
451     /*
452       Transfer a FPX pixels.
453     */
454     r=red_component->theData+(y % tile_height)*red_component->lineStride;
455     g=green_component->theData+(y % tile_height)*green_component->lineStride;
456     b=blue_component->theData+(y % tile_height)*blue_component->lineStride;
457     a=alpha_component->theData+(y % tile_height)*alpha_component->lineStride;
458     for (x=0; x < (ssize_t) image->columns; x++)
459     {
460       if (fpx_info.numberOfComponents > 2)
461         {
462           q->red=ScaleCharToQuantum(*r);
463           q->green=ScaleCharToQuantum(*g);
464           q->blue=ScaleCharToQuantum(*b);
465         }
466       else
467         {
468           index=ScaleCharToQuantum(*r);
469           indexes[x]=index;
470           q->red=index;
471           q->green=index;
472           q->blue=index;
473         }
474       SetOpacityPixelComponent(q,OpaqueOpacity);
475       if (image->matte != MagickFalse)
476         q->opacity=ScaleCharToQuantum(255-*a);
477       q++;
478       r+=red_component->columnStride;
479       g+=green_component->columnStride;
480       b+=blue_component->columnStride;
481       a+=alpha_component->columnStride;
482     }
483     if (SyncAuthenticPixels(image,exception) == MagickFalse)
484       break;
485     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
486                 image->rows);
487     if (status == MagickFalse)
488       break;
489   }
490   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
491   (void) FPX_CloseImage(flashpix);
492   FPX_ClearSystem();
493   return(GetFirstImageInList(image));
494 }
495 #endif
496 \f
497 /*
498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
499 %                                                                             %
500 %                                                                             %
501 %                                                                             %
502 %   R e g i s t e r F P X I m a g e                                           %
503 %                                                                             %
504 %                                                                             %
505 %                                                                             %
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507 %
508 %  RegisterFPXImage() adds attributes for the FPX image format to
509 %  the list of supported formats.  The attributes include the image format
510 %  tag, a method to read and/or write the format, whether the format
511 %  supports the saving of more than one frame to the same file or blob,
512 %  whether the format supports native in-memory I/O, and a brief
513 %  description of the format.
514 %
515 %  The format of the RegisterFPXImage method is:
516 %
517 %      size_t RegisterFPXImage(void)
518 %
519 */
520 ModuleExport size_t RegisterFPXImage(void)
521 {
522   MagickInfo
523     *entry;
524
525   entry=SetMagickInfo("FPX");
526 #if defined(MAGICKCORE_FPX_DELEGATE)
527   entry->decoder=(DecodeImageHandler *) ReadFPXImage;
528   entry->encoder=(EncodeImageHandler *) WriteFPXImage;
529 #endif
530   entry->adjoin=MagickFalse;
531   entry->seekable_stream=MagickTrue;
532   entry->blob_support=MagickFalse;
533   entry->magick=(IsImageFormatHandler *) IsFPX;
534   entry->description=ConstantString("FlashPix Format");
535   entry->module=ConstantString("FPX");
536   (void) RegisterMagickInfo(entry);
537   return(MagickImageCoderSignature);
538 }
539 \f
540 /*
541 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542 %                                                                             %
543 %                                                                             %
544 %                                                                             %
545 %   U n r e g i s t e r F P X I m a g e                                       %
546 %                                                                             %
547 %                                                                             %
548 %                                                                             %
549 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550 %
551 %  UnregisterFPXImage() removes format registrations made by the
552 %  FPX module from the list of supported formats.
553 %
554 %  The format of the UnregisterFPXImage method is:
555 %
556 %      UnregisterFPXImage(void)
557 %
558 */
559 ModuleExport void UnregisterFPXImage(void)
560 {
561   (void) UnregisterMagickInfo("FPX");
562 }
563 \f
564 #if defined(MAGICKCORE_FPX_DELEGATE)
565 /*
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 %                                                                             %
568 %                                                                             %
569 %                                                                             %
570 %   W r i t e F P X I m a g e                                                 %
571 %                                                                             %
572 %                                                                             %
573 %                                                                             %
574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575 %
576 %  WriteFPXImage() writes an image in the FlashPix image format.  This
577 %  method was contributed by BillR@corbis.com.
578 %
579 %  The format of the WriteFPXImage method is:
580 %
581 %      MagickBooleanType WriteFPXImage(const ImageInfo *image_info,Image *image)
582 %
583 %  A description of each parameter follows.
584 %
585 %    o image_info: the image info.
586 %
587 %    o image:  The image.
588 %
589 */
590
591 static void ColorTwistMultiply(FPXColorTwistMatrix first,
592   FPXColorTwistMatrix second,FPXColorTwistMatrix *color_twist)
593 {
594   /*
595     Matrix multiply.
596   */
597   assert(color_twist != (FPXColorTwistMatrix *) NULL);
598   color_twist->byy=(first.byy*second.byy)+(first.byc1*second.bc1y)+
599     (first.byc2*second.bc2y)+(first.dummy1_zero*second.dummy4_zero);
600   color_twist->byc1=(first.byy*second.byc1)+(first.byc1*second.bc1c1)+
601     (first.byc2*second.bc2c1)+(first.dummy1_zero*second.dummy5_zero);
602   color_twist->byc2=(first.byy*second.byc2)+(first.byc1*second.bc1c2)+
603     (first.byc2*second.bc2c2)+(first.dummy1_zero*second.dummy6_zero);
604   color_twist->dummy1_zero=(first.byy*second.dummy1_zero)+
605     (first.byc1*second.dummy2_zero)+(first.byc2*second.dummy3_zero)+
606     (first.dummy1_zero*second.dummy7_one);
607   color_twist->bc1y=(first.bc1y*second.byy)+(first.bc1c1*second.bc1y)+
608     (first.bc1c2*second.bc2y)+(first.dummy2_zero*second.dummy4_zero);
609   color_twist->bc1c1=(first.bc1y*second.byc1)+(first.bc1c1*second.bc1c1)+
610     (first.bc1c2*second.bc2c1)+(first.dummy2_zero*second.dummy5_zero);
611   color_twist->bc1c2=(first.bc1y*second.byc2)+(first.bc1c1*second.bc1c2)+
612     (first.bc1c2*second.bc2c2)+(first.dummy2_zero*second.dummy6_zero);
613   color_twist->dummy2_zero=(first.bc1y*second.dummy1_zero)+
614     (first.bc1c1*second.dummy2_zero)+(first.bc1c2*second.dummy3_zero)+
615     (first.dummy2_zero*second.dummy7_one);
616   color_twist->bc2y=(first.bc2y*second.byy)+(first.bc2c1*second.bc1y)+
617     (first.bc2c2*second.bc2y)+(first.dummy3_zero*second.dummy4_zero);
618   color_twist->bc2c1=(first.bc2y*second.byc1)+(first.bc2c1*second.bc1c1)+
619     (first.bc2c2*second.bc2c1)+(first.dummy3_zero*second.dummy5_zero);
620   color_twist->bc2c2=(first.bc2y*second.byc2)+(first.bc2c1*second.bc1c2)+
621     (first.bc2c2*second.bc2c2)+(first.dummy3_zero*second.dummy6_zero);
622   color_twist->dummy3_zero=(first.bc2y*second.dummy1_zero)+
623     (first.bc2c1*second.dummy2_zero)+(first.bc2c2*second.dummy3_zero)+
624     (first.dummy3_zero*second.dummy7_one);
625   color_twist->dummy4_zero=(first.dummy4_zero*second.byy)+
626     (first.dummy5_zero*second.bc1y)+(first.dummy6_zero*second.bc2y)+
627     (first.dummy7_one*second.dummy4_zero);
628   color_twist->dummy5_zero=(first.dummy4_zero*second.byc1)+
629     (first.dummy5_zero*second.bc1c1)+(first.dummy6_zero*second.bc2c1)+
630     (first.dummy7_one*second.dummy5_zero);
631   color_twist->dummy6_zero=(first.dummy4_zero*second.byc2)+
632     (first.dummy5_zero*second.bc1c2)+(first.dummy6_zero*second.bc2c2)+
633     (first.dummy7_one*second.dummy6_zero);
634   color_twist->dummy7_one=(first.dummy4_zero*second.dummy1_zero)+
635     (first.dummy5_zero*second.dummy2_zero)+
636     (first.dummy6_zero*second.dummy3_zero)+(first.dummy7_one*second.dummy7_one);
637 }
638
639 static void SetBrightness(double brightness,FPXColorTwistMatrix *color_twist)
640 {
641   FPXColorTwistMatrix
642     effect,
643     result;
644
645   /*
646     Set image brightness in color twist matrix.
647   */
648   assert(color_twist != (FPXColorTwistMatrix *) NULL);
649   brightness=sqrt((double) brightness);
650   effect.byy=brightness;
651   effect.byc1=0.0;
652   effect.byc2=0.0;
653   effect.dummy1_zero=0.0;
654   effect.bc1y=0.0;
655   effect.bc1c1=brightness;
656   effect.bc1c2=0.0;
657   effect.dummy2_zero=0.0;
658   effect.bc2y=0.0;
659   effect.bc2c1=0.0;
660   effect.bc2c2=brightness;
661   effect.dummy3_zero=0.0;
662   effect.dummy4_zero=0.0;
663   effect.dummy5_zero=0.0;
664   effect.dummy6_zero=0.0;
665   effect.dummy7_one=1.0;
666   ColorTwistMultiply(*color_twist,effect,&result);
667   *color_twist=result;
668 }
669
670 static void SetColorBalance(double red,double green,double blue,
671   FPXColorTwistMatrix *color_twist)
672 {
673   FPXColorTwistMatrix
674     blue_effect,
675     green_effect,
676     result,
677     rgb_effect,
678     rg_effect,
679     red_effect;
680
681   /*
682     Set image color balance in color twist matrix.
683   */
684   assert(color_twist != (FPXColorTwistMatrix *) NULL);
685   red=sqrt((double) red)-1.0;
686   green=sqrt((double) green)-1.0;
687   blue=sqrt((double) blue)-1.0;
688   red_effect.byy=1.0;
689   red_effect.byc1=0.0;
690   red_effect.byc2=0.299*red;
691   red_effect.dummy1_zero=0.0;
692   red_effect.bc1y=(-0.299)*red;
693   red_effect.bc1c1=1.0-0.299*red;
694   red_effect.bc1c2=(-0.299)*red;
695   red_effect.dummy2_zero=0.0;
696   red_effect.bc2y=0.701*red;
697   red_effect.bc2c1=0.0;
698   red_effect.bc2c2=1.0+0.402*red;
699   red_effect.dummy3_zero=0.0;
700   red_effect.dummy4_zero=0.0;
701   red_effect.dummy5_zero=0.0;
702   red_effect.dummy6_zero=0.0;
703   red_effect.dummy7_one=1.0;
704   green_effect.byy=1.0;
705   green_effect.byc1=(-0.114)*green;
706   green_effect.byc2=(-0.299)*green;
707   green_effect.dummy1_zero=0.0;
708   green_effect.bc1y=(-0.587)*green;
709   green_effect.bc1c1=1.0-0.473*green;
710   green_effect.bc1c2=0.299*green;
711   green_effect.dummy2_zero=0.0;
712   green_effect.bc2y=(-0.587)*green;
713   green_effect.bc2c1=0.114*green;
714   green_effect.bc2c2=1.0-0.288*green;
715   green_effect.dummy3_zero=0.0;
716   green_effect.dummy4_zero=0.0;
717   green_effect.dummy5_zero=0.0;
718   green_effect.dummy6_zero=0.0;
719   green_effect.dummy7_one=1.0;
720   blue_effect.byy=1.0;
721   blue_effect.byc1=0.114*blue;
722   blue_effect.byc2=0.0;
723   blue_effect.dummy1_zero=0.0;
724   blue_effect.bc1y=0.886*blue;
725   blue_effect.bc1c1=1.0+0.772*blue;
726   blue_effect.bc1c2=0.0;
727   blue_effect.dummy2_zero=0.0;
728   blue_effect.bc2y=(-0.114)*blue;
729   blue_effect.bc2c1=(-0.114)*blue;
730   blue_effect.bc2c2=1.0-0.114*blue;
731   blue_effect.dummy3_zero=0.0;
732   blue_effect.dummy4_zero=0.0;
733   blue_effect.dummy5_zero=0.0;
734   blue_effect.dummy6_zero=0.0;
735   blue_effect.dummy7_one=1.0;
736   ColorTwistMultiply(red_effect,green_effect,&rg_effect);
737   ColorTwistMultiply(rg_effect,blue_effect,&rgb_effect);
738   ColorTwistMultiply(*color_twist,rgb_effect,&result);
739   *color_twist=result;
740 }
741
742 static void SetSaturation(double saturation,FPXColorTwistMatrix *color_twist)
743 {
744   FPXColorTwistMatrix
745     effect,
746     result;
747
748   /*
749     Set image saturation in color twist matrix.
750   */
751   assert(color_twist != (FPXColorTwistMatrix *) NULL);
752   effect.byy=1.0;
753   effect.byc1=0.0;
754   effect.byc2=0.0;
755   effect.dummy1_zero=0.0;
756   effect.bc1y=0.0;
757   effect.bc1c1=saturation;
758   effect.bc1c2=0.0;
759   effect.dummy2_zero=0.0;
760   effect.bc2y=0.0;
761   effect.bc2c1=0.0;
762   effect.bc2c2=saturation;
763   effect.dummy3_zero=0.0;
764   effect.dummy4_zero=0.0;
765   effect.dummy5_zero=0.0;
766   effect.dummy6_zero=0.0;
767   effect.dummy7_one=1.0;
768   ColorTwistMultiply(*color_twist,effect,&result);
769   *color_twist=result;
770 }
771
772 static MagickBooleanType WriteFPXImage(const ImageInfo *image_info,Image *image)
773 {
774   FPXBackground
775     background_color;
776
777   FPXColorspace
778     colorspace =
779     {
780       TRUE, 4,
781       {
782         { NIFRGB_R, DATA_TYPE_UNSIGNED_BYTE },
783         { NIFRGB_G, DATA_TYPE_UNSIGNED_BYTE },
784         { NIFRGB_B, DATA_TYPE_UNSIGNED_BYTE },
785         { ALPHA, DATA_TYPE_UNSIGNED_BYTE }
786       }
787     };
788
789   const char
790     *comment,
791     *label;
792
793   FPXCompressionOption
794     compression;
795
796   FPXImageDesc
797     fpx_info;
798
799   FPXImageHandle
800     *flashpix;
801
802   FPXStatus
803     fpx_status;
804
805   FPXSummaryInformation
806     summary_info;
807
808   ssize_t
809     y;
810
811   MagickBooleanType
812     status;
813
814   QuantumInfo
815     *quantum_info;
816
817   QuantumType
818     quantum_type;
819
820   register const PixelPacket
821     *p;
822
823   register ssize_t
824     i;
825
826   size_t
827     length,
828     memory_limit;
829
830   unsigned char
831     *pixels;
832
833   unsigned int
834     tile_height,
835     tile_width;
836
837   /*
838     Open input file.
839   */
840   assert(image_info != (const ImageInfo *) NULL);
841   assert(image_info->signature == MagickSignature);
842   assert(image != (Image *) NULL);
843   assert(image->signature == MagickSignature);
844   if (image->debug != MagickFalse)
845     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
846   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
847   if (status == MagickFalse)
848     return(status);
849   (void) CloseBlob(image);
850   /*
851     Initialize FPX toolkit.
852   */
853   image->depth=8;
854   if (image->colorspace != RGBColorspace)
855     (void) TransformImageColorspace(image,RGBColorspace);
856   memory_limit=20000000;
857   fpx_status=FPX_SetToolkitMemoryLimit(&memory_limit);
858   if (fpx_status != FPX_OK)
859     ThrowWriterException(DelegateError,"UnableToInitializeFPXLibrary");
860   tile_width=64;
861   tile_height=64;
862   colorspace.numberOfComponents=3;
863   if (image->matte != MagickFalse)
864     colorspace.numberOfComponents=4;
865   if ((image_info->type != TrueColorType) &&
866       IsGrayImage(image,&image->exception))
867     {
868       colorspace.numberOfComponents=1;
869       colorspace.theComponents[0].myColor=MONOCHROME;
870     }
871   background_color.color1_value=0;
872   background_color.color2_value=0;
873   background_color.color3_value=0;
874   background_color.color4_value=0;
875   compression=NONE;
876   if (image->compression == JPEGCompression)
877     compression=JPEG_UNSPECIFIED;
878   if (image_info->compression == JPEGCompression)
879     compression=JPEG_UNSPECIFIED;
880   {
881 #if defined(macintosh)
882     FSSpec
883       fsspec;
884
885     FilenameToFSSpec(filename,&fsspec);
886     fpx_status=FPX_CreateImageByFilename((const FSSpec &) fsspec,image->columns,
887 #else
888     fpx_status=FPX_CreateImageByFilename(image->filename,image->columns,
889 #endif
890       image->rows,tile_width,tile_height,colorspace,background_color,
891       compression,&flashpix);
892   }
893   if (fpx_status != FPX_OK)
894     return(status);
895   if (compression == JPEG_UNSPECIFIED)
896     {
897       /*
898         Initialize the compression by quality for the entire image.
899       */
900       fpx_status=FPX_SetJPEGCompression(flashpix,(unsigned short)
901         image->quality == UndefinedCompressionQuality ? 75 : image->quality);
902       if (fpx_status != FPX_OK)
903         ThrowWriterException(DelegateError,"UnableToSetJPEGLevel");
904     }
905   /*
906     Set image summary info.
907   */
908   summary_info.title_valid=MagickFalse;
909   summary_info.subject_valid=MagickFalse;
910   summary_info.author_valid=MagickFalse;
911   summary_info.comments_valid=MagickFalse;
912   summary_info.keywords_valid=MagickFalse;
913   summary_info.OLEtemplate_valid=MagickFalse;
914   summary_info.last_author_valid=MagickFalse;
915   summary_info.rev_number_valid=MagickFalse;
916   summary_info.edit_time_valid=MagickFalse;
917   summary_info.last_printed_valid=MagickFalse;
918   summary_info.create_dtm_valid=MagickFalse;
919   summary_info.last_save_dtm_valid=MagickFalse;
920   summary_info.page_count_valid=MagickFalse;
921   summary_info.word_count_valid=MagickFalse;
922   summary_info.char_count_valid=MagickFalse;
923   summary_info.thumbnail_valid=MagickFalse;
924   summary_info.appname_valid=MagickFalse;
925   summary_info.security_valid=MagickFalse;
926   label=GetImageProperty(image,"label");
927   if (label != (const char *) NULL)
928     {
929       size_t
930         length;
931
932       /*
933         Note image label.
934       */
935       summary_info.title_valid=MagickTrue;
936       length=strlen(label);
937       summary_info.title.length=length;
938       if (~length >= MaxTextExtent)
939         summary_info.title.ptr=(unsigned char *) AcquireQuantumMemory(
940           length+MaxTextExtent,sizeof(*summary_info.title.ptr));
941       if (summary_info.title.ptr == (unsigned char *) NULL)
942         ThrowWriterException(DelegateError,"UnableToSetImageTitle");
943       (void) CopyMagickString((char *) summary_info.title.ptr,label,
944         MaxTextExtent);
945     }
946   comment=GetImageProperty(image,"comment");
947   if (comment != (const char *) NULL)
948     {
949       /*
950         Note image comment.
951       */
952       summary_info.comments_valid=MagickTrue;
953       summary_info.comments.ptr=(unsigned char *) AcquireString(comment);
954       summary_info.comments.length=strlen(comment);
955     }
956   fpx_status=FPX_SetSummaryInformation(flashpix,&summary_info);
957   if (fpx_status != FPX_OK)
958     ThrowWriterException(DelegateError,"UnableToSetSummaryInfo");
959   /*
960     Initialize FlashPix image description.
961   */
962   quantum_info=AcquireQuantumInfo(image_info,image);
963   if (quantum_info == (QuantumInfo *) NULL)
964     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
965   pixels=GetQuantumPixels(quantum_info);
966   fpx_info.numberOfComponents=colorspace.numberOfComponents;
967   for (i=0; i < (ssize_t) fpx_info.numberOfComponents; i++)
968   {
969     fpx_info.components[i].myColorType.myDataType=DATA_TYPE_UNSIGNED_BYTE;
970     fpx_info.components[i].horzSubSampFactor=1;
971     fpx_info.components[i].vertSubSampFactor=1;
972     fpx_info.components[i].columnStride=fpx_info.numberOfComponents;
973     fpx_info.components[i].lineStride=
974       image->columns*fpx_info.components[i].columnStride;
975     fpx_info.components[i].theData=pixels+i;
976   }
977   fpx_info.components[0].myColorType.myColor=fpx_info.numberOfComponents != 1
978     ? NIFRGB_R : MONOCHROME;
979   fpx_info.components[1].myColorType.myColor=NIFRGB_G;
980   fpx_info.components[2].myColorType.myColor=NIFRGB_B;
981   fpx_info.components[3].myColorType.myColor=ALPHA;
982   /*
983     Write image pixelss.
984   */
985   quantum_type=RGBQuantum;
986   if (image->matte != MagickFalse)
987     quantum_type=RGBAQuantum;
988   if (fpx_info.numberOfComponents == 1)
989     quantum_type=GrayQuantum;
990   for (y=0; y < (ssize_t) image->rows; y++)
991   {
992     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
993     if (p == (const PixelPacket *) NULL)
994       break;
995     length=ExportQuantumPixels(image,(const CacheView *) NULL,quantum_info,
996       quantum_type,pixels,&image->exception);
997     fpx_status=FPX_WriteImageLine(flashpix,&fpx_info);
998     if (fpx_status != FPX_OK)
999       break;
1000     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1001                 image->rows);
1002     if (status == MagickFalse)
1003       break;
1004   }
1005   quantum_info=DestroyQuantumInfo(quantum_info);
1006   if (image_info->view != (char *) NULL)
1007     {
1008       FPXAffineMatrix
1009         affine;
1010
1011       FPXColorTwistMatrix
1012         color_twist;
1013
1014       FPXContrastAdjustment
1015         contrast;
1016
1017       FPXFilteringValue
1018         sharpen;
1019
1020       FPXResultAspectRatio
1021         aspect_ratio;
1022
1023       FPXROI
1024         view_rect;
1025
1026       MagickBooleanType
1027         affine_valid,
1028         aspect_ratio_valid,
1029         color_twist_valid,
1030         contrast_valid,
1031         sharpen_valid,
1032         view_rect_valid;
1033
1034       /*
1035         Initialize default viewing parameters.
1036       */
1037       contrast=1.0;
1038       contrast_valid=MagickFalse;
1039       color_twist.byy=1.0;
1040       color_twist.byc1=0.0;
1041       color_twist.byc2=0.0;
1042       color_twist.dummy1_zero=0.0;
1043       color_twist.bc1y=0.0;
1044       color_twist.bc1c1=1.0;
1045       color_twist.bc1c2=0.0;
1046       color_twist.dummy2_zero=0.0;
1047       color_twist.bc2y=0.0;
1048       color_twist.bc2c1=0.0;
1049       color_twist.bc2c2=1.0;
1050       color_twist.dummy3_zero=0.0;
1051       color_twist.dummy4_zero=0.0;
1052       color_twist.dummy5_zero=0.0;
1053       color_twist.dummy6_zero=0.0;
1054       color_twist.dummy7_one=1.0;
1055       color_twist_valid=MagickFalse;
1056       sharpen=0.0;
1057       sharpen_valid=MagickFalse;
1058       aspect_ratio=(double) image->columns/image->rows;
1059       aspect_ratio_valid=MagickFalse;
1060       view_rect.left=(float) 0.1;
1061       view_rect.width=aspect_ratio-0.2;
1062       view_rect.top=(float) 0.1;
1063       view_rect.height=(float) 0.8; /* 1.0-0.2 */
1064       view_rect_valid=MagickFalse;
1065       affine.a11=1.0;
1066       affine.a12=0.0;
1067       affine.a13=0.0;
1068       affine.a14=0.0;
1069       affine.a21=0.0;
1070       affine.a22=1.0;
1071       affine.a23=0.0;
1072       affine.a24=0.0;
1073       affine.a31=0.0;
1074       affine.a32=0.0;
1075       affine.a33=1.0;
1076       affine.a34=0.0;
1077       affine.a41=0.0;
1078       affine.a42=0.0;
1079       affine.a43=0.0;
1080       affine.a44=1.0;
1081       affine_valid=MagickFalse;
1082       if (0)
1083         {
1084           /*
1085             Color color twist.
1086           */
1087           SetBrightness(0.5,&color_twist);
1088           SetSaturation(0.5,&color_twist);
1089           SetColorBalance(0.5,1.0,1.0,&color_twist);
1090           color_twist_valid=MagickTrue;
1091         }
1092       if (affine_valid)
1093         {
1094           fpx_status=FPX_SetImageAffineMatrix(flashpix,&affine);
1095           if (fpx_status != FPX_OK)
1096             ThrowWriterException(DelegateError,"UnableToSetAffineMatrix");
1097         }
1098       if (aspect_ratio_valid)
1099         {
1100           fpx_status=FPX_SetImageResultAspectRatio(flashpix,&aspect_ratio);
1101           if (fpx_status != FPX_OK)
1102             ThrowWriterException(DelegateError,"UnableToSetAspectRatio");
1103         }
1104       if (color_twist_valid)
1105         {
1106           fpx_status=FPX_SetImageColorTwistMatrix(flashpix,&color_twist);
1107           if (fpx_status != FPX_OK)
1108             ThrowWriterException(DelegateError,"UnableToSetColorTwist");
1109         }
1110       if (contrast_valid)
1111         {
1112           fpx_status=FPX_SetImageContrastAdjustment(flashpix,&contrast);
1113           if (fpx_status != FPX_OK)
1114             ThrowWriterException(DelegateError,"UnableToSetContrast");
1115         }
1116       if (sharpen_valid)
1117         {
1118           fpx_status=FPX_SetImageFilteringValue(flashpix,&sharpen);
1119           if (fpx_status != FPX_OK)
1120             ThrowWriterException(DelegateError,"UnableToSetFilteringValue");
1121         }
1122       if (view_rect_valid)
1123         {
1124           fpx_status=FPX_SetImageROI(flashpix,&view_rect);
1125           if (fpx_status != FPX_OK)
1126             ThrowWriterException(DelegateError,"UnableToSetRegionOfInterest");
1127         }
1128     }
1129   (void) FPX_CloseImage(flashpix);
1130   FPX_ClearSystem();
1131   return(MagickTrue);
1132 }
1133 #endif