root / ImageMagick / trunk / coders / fpx.c

Revision 12033, 36.1 kB (checked in by cristy, 7 weeks ago)
Line 
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-2008 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
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/color.h"
47#include "magick/color-private.h"
48#include "magick/colorspace.h"
49#include "magick/constitute.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/geometry.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/magick.h"
57#include "magick/memory_.h"
58#include "magick/monitor.h"
59#include "magick/monitor-private.h"
60#include "magick/pixel.h"
61#include "magick/quantum-private.h"
62#include "magick/static.h"
63#include "magick/string_.h"
64#include "magick/module.h"
65#if defined(MAGICKCORE_FPX_DELEGATE)
66#if !defined(vms) && !defined(macintosh) && !defined(__WINDOWS__)
67#include <fpxlib.h>
68#else
69#include "Fpxlib.h"
70#endif
71#endif
72
73#if defined(MAGICKCORE_FPX_DELEGATE)
74/*
75  Forward declarations.
76*/
77static MagickBooleanType
78  WriteFPXImage(const ImageInfo *,Image *);
79#endif
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83%                                                                             %
84%                                                                             %
85%                                                                             %
86%   I s F P X                                                                 %
87%                                                                             %
88%                                                                             %
89%                                                                             %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
92%  IsFPX() returns MagickTrue if the image format type, identified by the
93%  magick string, is FPX.
94%
95%  The format of the IsFPX method is:
96%
97%      MagickBooleanType IsFPX(const unsigned char *magick,const size_t length)
98%
99%  A description of each parameter follows:
100%
101%    o magick: This string is generally the first few bytes of an image file
102%      or blob.
103%
104%    o length: Specifies the length of the magick string.
105%
106*/
107static 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
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*/
143static 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  long
173    y;
174
175  MagickBooleanType
176    status;
177
178  register IndexPacket
179    *indexes;
180
181  register long
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  unsigned long
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 != (unsigned long) ((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  if (SetImageExtent(image,0,0) == MagickFalse)
386    {
387      (void) FPX_CloseImage(flashpix);
388      FPX_ClearSystem();
389      InheritException(exception,&image->exception);
390      return(DestroyImageList(image));
391    }
392  /*
393    Allocate memory for the image and pixel buffer.
394  */
395  pixels=(unsigned char *) AcquireQuantumMemory(image->columns,(tile_height+
396    1UL)*colorspace.numberOfComponents*sizeof(*pixels));
397  if (pixels == (unsigned char *) NULL)
398    {
399      FPX_ClearSystem();
400      (void) FPX_CloseImage(flashpix);
401      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
402    }
403  /*
404    Initialize FlashPix image description.
405  */
406  fpx_info.numberOfComponents=colorspace.numberOfComponents;
407  for (i=0; i < 4; i++)
408  {
409    fpx_info.components[i].myColorType.myDataType=DATA_TYPE_UNSIGNED_BYTE;
410    fpx_info.components[i].horzSubSampFactor=1;
411    fpx_info.components[i].vertSubSampFactor=1;
412    fpx_info.components[i].columnStride=fpx_info.numberOfComponents;
413    fpx_info.components[i].lineStride=image->columns*
414      fpx_info.components[i].columnStride;
415    fpx_info.components[i].theData=pixels+i;
416  }
417  fpx_info.components[0].myColorType.myColor=fpx_info.numberOfComponents > 2 ?
418    NIFRGB_R : MONOCHROME;
419  red_component=(&fpx_info.components[0]);
420  fpx_info.components[1].myColorType.myColor=fpx_info.numberOfComponents > 2 ?
421    NIFRGB_G : ALPHA;
422  green_component=(&fpx_info.components[1]);
423  fpx_info.components[2].myColorType.myColor=NIFRGB_B;
424  blue_component=(&fpx_info.components[2]);
425  fpx_info.components[3].myColorType.myColor=ALPHA;
426  alpha_component=(&fpx_info.components[fpx_info.numberOfComponents-1]);
427  FPX_SetResampleMethod(FPX_LINEAR_INTERPOLATION);
428  /*
429    Initialize image pixels.
430  */
431  for (y=0; y < (long) image->rows; y++)
432  {
433    q=SetImagePixels(image,0,y,image->columns,1);
434    if (q == (PixelPacket *) NULL)
435      break;
436    indexes=GetIndexes(image);
437    if ((y % tile_height) == 0)
438      {
439        /*
440          Read FPX image tile (with or without viewing affine)..
441        */
442        if (image_info->view != (char *) NULL)
443          fpx_status=FPX_ReadImageRectangle(flashpix,0,y,image->columns,y+
444            tile_height-1,scene,&fpx_info);
445        else
446          fpx_status=FPX_ReadImageTransformRectangle(flashpix,0.0F,
447            (float) y/image->rows,(float) image->columns/image->rows,
448            (float) (y+tile_height-1)/image->rows,(long) image->columns,
449            (long) tile_height,&fpx_info);
450        if (fpx_status == FPX_LOW_MEMORY_ERROR)
451          {
452            pixels=(unsigned char *) RelinquishMagickMemory(pixels);
453            (void) FPX_CloseImage(flashpix);
454            FPX_ClearSystem();
455            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
456          }
457      }
458    /*
459      Transfer a FPX pixels.
460    */
461    r=red_component->theData+(y % tile_height)*red_component->lineStride;
462    g=green_component->theData+(y % tile_height)*green_component->lineStride;
463    b=blue_component->theData+(y % tile_height)*blue_component->lineStride;
464    a=alpha_component->theData+(y % tile_height)*alpha_component->lineStride;
465    for (x=0; x < (long) image->columns; x++)
466    {
467      if (fpx_info.numberOfComponents > 2)
468        {
469          q->red=ScaleCharToQuantum(*r);
470          q->green=ScaleCharToQuantum(*g);
471          q->blue=ScaleCharToQuantum(*b);
472        }
473      else
474        {
475          index=ScaleCharToQuantum(*r);
476          indexes[x]=index;
477          q->red=index;
478          q->green=index;
479          q->blue=index;
480        }
481      if (image->matte != MagickFalse)
482        q->opacity=ScaleCharToQuantum(255-*a);
483      q++;
484      r+=red_component->columnStride;
485      g+=green_component->columnStride;
486      b+=blue_component->columnStride;
487      a+=alpha_component->columnStride;
488    }
489    if (SyncImagePixels(image) == MagickFalse)
490      break;
491    status=SetImageProgress(image,LoadImageTag,y,image->rows);
492    if (status == MagickFalse)
493      break;
494  }
495  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
496  (void) FPX_CloseImage(flashpix);
497  FPX_ClearSystem();
498  return(GetFirstImageInList(image));
499}
500#endif
501
502/*
503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504%                                                                             %
505%                                                                             %
506%                                                                             %
507%   R e g i s t e r F P X I m a g e                                           %
508%                                                                             %
509%                                                                             %
510%                                                                             %
511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512%
513%  RegisterFPXImage() adds attributes for the FPX image format to
514%  the list of supported formats.  The attributes include the image format
515%  tag, a method to read and/or write the format, whether the format
516%  supports the saving of more than one frame to the same file or blob,
517%  whether the format supports native in-memory I/O, and a brief
518%  description of the format.
519%
520%  The format of the RegisterFPXImage method is:
521%
522%      unsigned long RegisterFPXImage(void)
523%
524*/
525ModuleExport unsigned long RegisterFPXImage(void)
526{
527  MagickInfo
528    *entry;
529
530  entry=SetMagickInfo("FPX");
531#if defined(MAGICKCORE_FPX_DELEGATE)
532  entry->decoder=(DecodeImageHandler *) ReadFPXImage;
533  entry->encoder=(EncodeImageHandler *) WriteFPXImage;
534#endif
535  entry->adjoin=MagickFalse;
536  entry->seekable_stream=MagickTrue;
537  entry->blob_support=MagickFalse;
538  entry->magick=(IsImageFormatHandler *) IsFPX;
539  entry->description=ConstantString("FlashPix Format");
540  entry->module=ConstantString("FPX");
541  (void) RegisterMagickInfo(entry);
542  return(MagickImageCoderSignature);
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547%                                                                             %
548%                                                                             %
549%                                                                             %
550%   U n r e g i s t e r F P X I m a g e                                       %
551%                                                                             %
552%                                                                             %
553%                                                                             %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556%  UnregisterFPXImage() removes format registrations made by the
557%  FPX module from the list of supported formats.
558%
559%  The format of the UnregisterFPXImage method is:
560%
561%      UnregisterFPXImage(void)
562%
563*/
564ModuleExport void UnregisterFPXImage(void)
565{
566  (void) UnregisterMagickInfo("FPX");
567}
568
569#if defined(MAGICKCORE_FPX_DELEGATE)
570/*
571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572%                                                                             %
573%                                                                             %
574%                                                                             %
575%   W r i t e F P X I m a g e                                                 %
576%                                                                             %
577%                                                                             %
578%                                                                             %
579%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
580%
581%  WriteFPXImage() writes an image in the FlashPix image format.  This
582%  method was contributed by BillR@corbis.com.
583%
584%  The format of the WriteFPXImage method is:
585%
586%      MagickBooleanType WriteFPXImage(const ImageInfo *image_info,Image *image)
587%
588%  A description of each parameter follows.
589%
590%    o image_info: the image info.
591%
592%    o image:  The image.
593%
594*/
595
596static void ColorTwistMultiply(FPXColorTwistMatrix first,
597  FPXColorTwistMatrix second,FPXColorTwistMatrix *color_twist)
598{
599  /*
600    Matrix multiply.
601  */
602  assert(color_twist != (FPXColorTwistMatrix *) NULL);
603  color_twist->byy=(first.byy*second.byy)+(first.byc1*second.bc1y)+
604    (first.byc2*second.bc2y)+(first.dummy1_zero*second.dummy4_zero);
605  color_twist->byc1=(first.byy*second.byc1)+(first.byc1*second.bc1c1)+
606    (first.byc2*second.bc2c1)+(first.dummy1_zero*second.dummy5_zero);
607  color_twist->byc2=(first.byy*second.byc2)+(first.byc1*second.bc1c2)+
608    (first.byc2*second.bc2c2)+(first.dummy1_zero*second.dummy6_zero);
609  color_twist->dummy1_zero=(first.byy*second.dummy1_zero)+
610    (first.byc1*second.dummy2_zero)+(first.byc2*second.dummy3_zero)+
611    (first.dummy1_zero*second.dummy7_one);
612  color_twist->bc1y=(first.bc1y*second.byy)+(first.bc1c1*second.bc1y)+
613    (first.bc1c2*second.bc2y)+(first.dummy2_zero*second.dummy4_zero);
614  color_twist->bc1c1=(first.bc1y*second.byc1)+(first.bc1c1*second.bc1c1)+
615    (first.bc1c2*second.bc2c1)+(first.dummy2_zero*second.dummy5_zero);
616  color_twist->bc1c2=(first.bc1y*second.byc2)+(first.bc1c1*second.bc1c2)+
617    (first.bc1c2*second.bc2c2)+(first.dummy2_zero*second.dummy6_zero);
618  color_twist->dummy2_zero=(first.bc1y*second.dummy1_zero)+
619    (first.bc1c1*second.dummy2_zero)+(first.bc1c2*second.dummy3_zero)+
620    (first.dummy2_zero*second.dummy7_one);
621  color_twist->bc2y=(first.bc2y*second.byy)+(first.bc2c1*second.bc1y)+
622    (first.bc2c2*second.bc2y)+(first.dummy3_zero*second.dummy4_zero);
623  color_twist->bc2c1=(first.bc2y*second.byc1)+(first.bc2c1*second.bc1c1)+
624    (first.bc2c2*second.bc2c1)+(first.dummy3_zero*second.dummy5_zero);
625  color_twist->bc2c2=(first.bc2y*second.byc2)+(first.bc2c1*second.bc1c2)+
626    (first.bc2c2*second.bc2c2)+(first.dummy3_zero*second.dummy6_zero);
627  color_twist->dummy3_zero=(first.bc2y*second.dummy1_zero)+
628    (first.bc2c1*second.dummy2_zero)+(first.bc2c2*second.dummy3_zero)+
629    (first.dummy3_zero*second.dummy7_one);
630  color_twist->dummy4_zero=(first.dummy4_zero*second.byy)+
631    (first.dummy5_zero*second.bc1y)+(first.dummy6_zero*second.bc2y)+
632    (first.dummy7_one*second.dummy4_zero);
633  color_twist->dummy5_zero=(first.dummy4_zero*second.byc1)+
634    (first.dummy5_zero*second.bc1c1)+(first.dummy6_zero*second.bc2c1)+
635    (first.dummy7_one*second.dummy5_zero);
636  color_twist->dummy6_zero=(first.dummy4_zero*second.byc2)+
637    (first.dummy5_zero*second.bc1c2)+(first.dummy6_zero*second.bc2c2)+
638    (first.dummy7_one*second.dummy6_zero);
639  color_twist->dummy7_one=(first.dummy4_zero*second.dummy1_zero)+
640    (first.dummy5_zero*second.dummy2_zero)+
641    (first.dummy6_zero*second.dummy3_zero)+(first.dummy7_one*second.dummy7_one);
642}
643
644static void SetBrightness(double brightness,FPXColorTwistMatrix *color_twist)
645{
646  FPXColorTwistMatrix
647    effect,
648    result;
649
650  /*
651    Set image brightness in color twist matrix.
652  */
653  assert(color_twist != (FPXColorTwistMatrix *) NULL);
654  brightness=sqrt((double) brightness);
655  effect.byy=brightness;
656  effect.byc1=0.0;
657  effect.byc2=0.0;
658  effect.dummy1_zero=0.0;
659  effect.bc1y=0.0;
660  effect.bc1c1=brightness;
661  effect.bc1c2=0.0;
662  effect.dummy2_zero=0.0;
663  effect.bc2y=0.0;
664  effect.bc2c1=0.0;
665  effect.bc2c2=brightness;
666  effect.dummy3_zero=0.0;
667  effect.dummy4_zero=0.0;
668  effect.dummy5_zero=0.0;
669  effect.dummy6_zero=0.0;
670  effect.dummy7_one=1.0;
671  ColorTwistMultiply(*color_twist,effect,&result);
672  *color_twist=result;
673}
674
675static void SetColorBalance(double red,double green,double blue,
676  FPXColorTwistMatrix *color_twist)
677{
678  FPXColorTwistMatrix
679    blue_effect,
680    green_effect,
681    result,
682    rgb_effect,
683    rg_effect,
684    red_effect;
685
686  /*
687    Set image color balance in color twist matrix.
688  */
689  assert(color_twist != (FPXColorTwistMatrix *) NULL);
690  red=sqrt((double) red)-1.0;
691  green=sqrt((double) green)-1.0;
692  blue=sqrt((double) blue)-1.0;
693  red_effect.byy=1.0;
694  red_effect.byc1=0.0;
695  red_effect.byc2=0.299*red;
696  red_effect.dummy1_zero=0.0;
697  red_effect.bc1y=(-0.299)*red;
698  red_effect.bc1c1=1.0-0.299*red;
699  red_effect.bc1c2=(-0.299)*red;
700  red_effect.dummy2_zero=0.0;
701  red_effect.bc2y=0.701*red;
702  red_effect.bc2c1=0.0;
703  red_effect.bc2c2=1.0+0.402*red;
704  red_effect.dummy3_zero=0.0;
705  red_effect.dummy4_zero=0.0;
706  red_effect.dummy5_zero=0.0;
707  red_effect.dummy6_zero=0.0;
708  red_effect.dummy7_one=1.0;
709  green_effect.byy=1.0;
710  green_effect.byc1=(-0.114)*green;
711  green_effect.byc2=(-0.299)*green;
712  green_effect.dummy1_zero=0.0;
713  green_effect.bc1y=(-0.587)*green;
714  green_effect.bc1c1=1.0-0.473*green;
715  green_effect.bc1c2=0.299*green;
716  green_effect.dummy2_zero=0.0;
717  green_effect.bc2y=(-0.587)*green;
718  green_effect.bc2c1=0.114*green;
719  green_effect.bc2c2=1.0-0.288*green;
720  green_effect.dummy3_zero=0.0;
721  green_effect.dummy4_zero=0.0;
722  green_effect.dummy5_zero=0.0;
723  green_effect.dummy6_zero=0.0;
724  green_effect.dummy7_one=1.0;
725  blue_effect.byy=1.0;
726  blue_effect.byc1=0.114*blue;
727  blue_effect.byc2=0.0;
728  blue_effect.dummy1_zero=0.0;
729  blue_effect.bc1y=0.886*blue;
730  blue_effect.bc1c1=1.0+0.772*blue;
731  blue_effect.bc1c2=0.0;
732  blue_effect.dummy2_zero=0.0;
733  blue_effect.bc2y=(-0.114)*blue;
734  blue_effect.bc2c1=(-0.114)*blue;
735  blue_effect.bc2c2=1.0-0.114*blue;
736  blue_effect.dummy3_zero=0.0;
737  blue_effect.dummy4_zero=0.0;
738  blue_effect.dummy5_zero=0.0;
739  blue_effect.dummy6_zero=0.0;
740  blue_effect.dummy7_one=1.0;
741  ColorTwistMultiply(red_effect,green_effect,&rg_effect);
742  ColorTwistMultiply(rg_effect,blue_effect,&rgb_effect);
743  ColorTwistMultiply(*color_twist,rgb_effect,&result);
744  *color_twist=result;
745}
746
747static void SetSaturation(double saturation,FPXColorTwistMatrix *color_twist)
748{
749  FPXColorTwistMatrix
750    effect,
751    result;
752
753  /*
754    Set image saturation in color twist matrix.
755  */
756  assert(color_twist != (FPXColorTwistMatrix *) NULL);
757  effect.byy=1.0;
758  effect.byc1=0.0;
759  effect.byc2=0.0;
760  effect.dummy1_zero=0.0;
761  effect.bc1y=0.0;
762  effect.bc1c1=saturation;
763  effect.bc1c2=0.0;
764  effect.dummy2_zero=0.0;
765  effect.bc2y=0.0;
766  effect.bc2c1=0.0;
767  effect.bc2c2=saturation;
768  effect.dummy3_zero=0.0;
769  effect.dummy4_zero=0.0;
770  effect.dummy5_zero=0.0;
771  effect.dummy6_zero=0.0;
772  effect.dummy7_one=1.0;
773  ColorTwistMultiply(*color_twist,effect,&result);
774  *color_twist=result;
775}
776
777static MagickBooleanType WriteFPXImage(const ImageInfo *image_info,Image *image)
778{
779  FPXBackground
780    background_color;
781
782  FPXColorspace
783    colorspace =
784    {
785      TRUE, 4,
786      {
787        { NIFRGB_R, DATA_TYPE_UNSIGNED_BYTE },
788        { NIFRGB_G, DATA_TYPE_UNSIGNED_BYTE },
789        { NIFRGB_B, DATA_TYPE_UNSIGNED_BYTE },
790        { ALPHA, DATA_TYPE_UNSIGNED_BYTE }
791      }
792    };
793
794  const char
795    *comment,
796    *label;
797
798  FPXCompressionOption
799    compression;
800
801  FPXImageDesc
802    fpx_info;
803
804  FPXImageHandle
805    *flashpix;
806
807  FPXStatus
808    fpx_status;
809
810  FPXSummaryInformation
811    summary_info;
812
813  long
814    y;
815
816  MagickBooleanType
817    status;
818
819  QuantumInfo
820    *quantum_info;
821
822  QuantumType
823    quantum_type;
824
825  register const PixelPacket
826    *p;
827
828  register long
829    i;
830
831  size_t
832    length,
833    memory_limit;
834
835  unsigned char
836    *pixels;
837
838  unsigned int
839    tile_height,
840    tile_width;
841
842  /*
843    Open input file.
844  */
845  assert(image_info != (const ImageInfo *) NULL);
846  assert(image_info->signature == MagickSignature);
847  assert(image != (Image *) NULL);
848  assert(image->signature == MagickSignature);
849  if (image->debug != MagickFalse)
850    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
851  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
852  if (status == MagickFalse)
853    return(status);
854  (void) CloseBlob(image);
855  /*
856    Initialize FPX toolkit.
857  */
858  image->depth=8;
859  if (image->colorspace != RGBColorspace)
860    (void) SetImageColorspace(image,RGBColorspace);
861  memory_limit=20000000;
862  fpx_status=FPX_SetToolkitMemoryLimit(&memory_limit);
863  if (fpx_status != FPX_OK)
864    ThrowWriterException(DelegateError,"UnableToInitializeFPXLibrary");
865  tile_width=64;
866  tile_height=64;
867  colorspace.numberOfComponents=3;
868  if (image->matte != MagickFalse)
869    colorspace.numberOfComponents=4;
870  if ((image_info->type != TrueColorType) &&
871      IsGrayImage(image,&image->exception))
872    {
873      colorspace.numberOfComponents=1;
874      colorspace.theComponents[0].myColor=MONOCHROME;
875    }
876  background_color.color1_value=0;
877  background_color.color2_value=0;
878  background_color.color3_value=0;
879  background_color.color4_value=0;
880  compression=NONE;
881  if (image_info->compression == JPEGCompression)
882    compression=JPEG_UNSPECIFIED;
883  {
884#if defined(macintosh)
885    FSSpec
886      fsspec;
887
888    FilenameToFSSpec(filename,&fsspec);
889    fpx_status=FPX_CreateImageByFilename((const FSSpec &) fsspec,image->columns,
890#else
891    fpx_status=FPX_CreateImageByFilename(image->filename,image->columns,
892#endif
893      image->rows,tile_width,tile_height,colorspace,background_color,
894      compression,&flashpix);
895  }
896  if (fpx_status != FPX_OK)
897    return(status);
898  if (compression == JPEG_UNSPECIFIED)
899    {
900      /*
901        Initialize the compression by quality for the entire image.
902      */
903      fpx_status=FPX_SetJPEGCompression(flashpix,(unsigned short)
904        image->quality == UndefinedCompressionQuality ? 75 : image->quality);
905      if (fpx_status != FPX_OK)
906        ThrowWriterException(DelegateError,"UnableToSetJPEGLevel");
907    }
908  /*
909    Set image summary info.
910  */
911  summary_info.title_valid=MagickFalse;
912  summary_info.subject_valid=MagickFalse;
913  summary_info.author_valid=MagickFalse;
914  summary_info.comments_valid=MagickFalse;
915  summary_info.keywords_valid=MagickFalse;
916  summary_info.OLEtemplate_valid=MagickFalse;
917  summary_info.last_author_valid=MagickFalse;
918  summary_info.rev_number_valid=MagickFalse;
919  summary_info.edit_time_valid=MagickFalse;
920  summary_info.last_printed_valid=MagickFalse;
921  summary_info.create_dtm_valid=MagickFalse;
922  summary_info.last_save_dtm_valid=MagickFalse;
923  summary_info.page_count_valid=MagickFalse;
924  summary_info.word_count_valid=MagickFalse;
925  summary_info.char_count_valid=MagickFalse;
926  summary_info.thumbnail_valid=MagickFalse;
927  summary_info.appname_valid=MagickFalse;
928  summary_info.security_valid=MagickFalse;
929  label=GetImageProperty(image,"label");
930  if (label != (const char *) NULL)
931    {
932      size_t
933        length;
934
935      /*
936        Note image label.
937      */
938      summary_info.title_valid=MagickTrue;
939      length=strlen(label);
940      summary_info.title.length=length;
941      if (~length >= MaxTextExtent)
942        summary_info.title.ptr=(unsigned char *) AcquireQuantumMemory(
943          length+MaxTextExtent,sizeof(*summary_info.title.ptr));
944      if (summary_info.title.ptr == (unsigned char *) NULL)
945        ThrowWriterException(DelegateError,"UnableToSetImageTitle");
946      (void) CopyMagickString((char *) summary_info.title.ptr,label,
947        MaxTextExtent);
948    }
949  comment=GetImageProperty(image,"comment");
950  if (comment != (const char *) NULL)
951    {
952      /*
953        Note image comment.
954      */
955      summary_info.comments_valid=MagickTrue;
956      summary_info.comments.ptr=(unsigned char *) AcquireString(comment);
957      summary_info.comments.length=strlen(comment);
958    }
959  fpx_status=FPX_SetSummaryInformation(flashpix,&summary_info);
960  if (fpx_status != FPX_OK)
961    ThrowWriterException(DelegateError,"UnableToSetSummaryInfo");
962  /*
963    Initialize FlashPix image description.
964  */
965  quantum_info=AcquireQuantumInfo(image_info,image);
966  pixels=GetQuantumPixels(quantum_info);
967  fpx_info.numberOfComponents=colorspace.numberOfComponents;
968  for (i=0; i < (long) fpx_info.numberOfComponents; i++)
969  {
970    fpx_info.components[i].myColorType.myDataType=DATA_TYPE_UNSIGNED_BYTE;
971    fpx_info.components[i].horzSubSampFactor=1;
972    fpx_info.components[i].vertSubSampFactor=1;
973    fpx_info.components[i].columnStride=fpx_info.numberOfComponents;
974    fpx_info.components[i].lineStride=
975      image->columns*fpx_info.components[i].columnStride;
976    fpx_info.components[i].theData=pixels+i;
977  }
978  fpx_info.components[0].myColorType.myColor=fpx_info.numberOfComponents != 1
979    ? NIFRGB_R : MONOCHROME;
980  fpx_info.components[1].myColorType.myColor=NIFRGB_G;
981  fpx_info.components[2].myColorType.myColor=NIFRGB_B;
982  fpx_info.components[3].myColorType.myColor=ALPHA;
983  /*
984    Write image pixelss.
985  */
986  quantum_type=RGBQuantum;
987  if (image->matte != MagickFalse)
988    quantum_type=RGBAQuantum;
989  if (fpx_info.numberOfComponents == 1)
990    quantum_type=GrayQuantum;
991  for (y=0; y < (long) image->rows; y++)
992  {
993    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
994    if (p == (const PixelPacket *) NULL)
995      break;
996    length=ExportQuantumPixels(image,quantum_info,quantum_type,pixels);
997    fpx_status=FPX_WriteImageLine(flashpix,&fpx_info);
998    if (fpx_status != FPX_OK)
999      break;
1000    status=SetImageProgress(image,SaveImageTag,y,image->rows);
1001    if (status == MagickFalse)
1002      break;
1003  }
1004  quantum_info=DestroyQuantumInfo(quantum_info);
1005  if (image_info->view != (char *) NULL)
1006    {
1007      FPXAffineMatrix
1008        affine;
1009
1010      FPXColorTwistMatrix
1011        color_twist;
1012
1013      FPXContrastAdjustment
1014        contrast;
1015
1016      FPXFilteringValue
1017        sharpen;
1018
1019      FPXResultAspectRatio
1020        aspect_ratio;
1021
1022      FPXROI
1023        view_rect;
1024
1025      MagickBooleanType
1026        affine_valid,
1027        aspect_ratio_valid,
1028        color_twist_valid,
1029        contrast_valid,
1030        sharpen_valid,
1031        view_rect_valid;
1032
1033      /*
1034        Initialize default viewing parameters.
1035      */
1036      contrast=1.0;
1037      contrast_valid=MagickFalse;
1038      color_twist.byy=1.0;
1039      color_twist.byc1=0.0;
1040      color_twist.byc2=0.0;
1041      color_twist.dummy1_zero=0.0;
1042      color_twist.bc1y=0.0;
1043      color_twist.bc1c1=1.0;
1044      color_twist.bc1c2=0.0;
1045      color_twist.dummy2_zero=0.0;
1046      color_twist.bc2y=0.0;
1047      color_twist.bc2c1=0.0;
1048      color_twist.bc2c2=1.0;
1049      color_twist.dummy3_zero=0.0;
1050      color_twist.dummy4_zero=0.0;
1051      color_twist.dummy5_zero=0.0;
1052      color_twist.dummy6_zero=0.0;
1053      color_twist.dummy7_one=1.0;
1054      color_twist_valid=MagickFalse;
1055      sharpen=0.0;
1056      sharpen_valid=MagickFalse;
1057      aspect_ratio=(double) image->columns/image->rows;
1058      aspect_ratio_valid=MagickFalse;
1059      view_rect.left=(float) 0.1;
1060      view_rect.width=aspect_ratio-0.2;
1061      view_rect.top=(float) 0.1;
1062      view_rect.height=(float) 0.8; /* 1.0-0.2 */
1063      view_rect_valid=MagickFalse;
1064      affine.a11=1.0;
1065      affine.a12=0.0;