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