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