diff --git a/coders/jp2.c b/coders/jp2.c
new file mode 100644
index 0000000..152fbe8
--- /dev/null
+++ b/coders/jp2.c
@@ -0,0 +1,968 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% JJJ PPPP 222 %
+% J P P 2 2 %
+% J PPPP 22 %
+% J J P 2 %
+% JJ P 22222 %
+% %
+% %
+% Read/Write JPEG-2000 Image Format %
+% %
+% John Cristy %
+% Nathan Brown %
+% June 2001 %
+% %
+% %
+% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
+% dedicated to making software imaging solutions freely available. %
+% %
+% You may not use this file except in compliance with the License. You may %
+% obtain a copy of the License at %
+% %
+% http://www.imagemagick.org/script/license.php %
+% %
+% Unless required by applicable law or agreed to in writing, software %
+% distributed under the License is distributed on an "AS IS" BASIS, %
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
+% See the License for the specific language governing permissions and %
+% limitations under the License. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/colorspace.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/profile.h"
+#include "magick/quantum-private.h"
+#include "magick/static.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/module.h"
+#if defined(MAGICKCORE_JP2_DELEGATE)
+#ifndef JAS_IMAGE_CM_GRAY
+#define JAS_IMAGE_CM_GRAY JAS_IMAGE_CS_GRAY
+#endif
+#ifndef JAS_IMAGE_CM_RGB
+#define JAS_IMAGE_CM_RGB JAS_IMAGE_CS_RGB
+#endif
+#if !defined(uchar)
+#define uchar unsigned char
+#endif
+#if !defined(ushort)
+#define ushort unsigned short
+#endif
+#if !defined(uint)
+#define uint unsigned int
+#endif
+#if !defined(longlong)
+#define longlong long long
+#endif
+#if !defined(ulonglong)
+#define ulonglong unsigned long long
+#endif
+
+#include "jasper/jasper.h"
+#endif
+
+/*
+ Forward declarations.
+*/
+#if defined(MAGICKCORE_JP2_DELEGATE)
+static MagickBooleanType
+ WriteJP2Image(const ImageInfo *,Image *);
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s J P 2 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsJP2() returns MagickTrue if the image format type, identified by the
+% magick string, is JP2.
+%
+% The format of the IsJP2 method is:
+%
+% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o magick: compare image format pattern against these bytes.
+%
+% o length: Specifies the length of the magick string.
+%
+*/
+static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
+{
+ if (length < 9)
+ return(MagickFalse);
+ if (memcmp(magick+4,"\152\120\040\040\015",5) == 0)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s J P C %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsJPC()() returns MagickTrue if the image format type, identified by the
+% magick string, is JPC.
+%
+% The format of the IsJPC method is:
+%
+% MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o magick: compare image format pattern against these bytes.
+%
+% o length: Specifies the length of the magick string.
+%
+*/
+static MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
+{
+ if (length < 2)
+ return(MagickFalse);
+ if (memcmp(magick,"\377\117",2) == 0)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d J P 2 I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
+% codestream (JPC) image file and returns it. It allocates the memory
+% necessary for the new Image structure and returns a pointer to the new
+% image or set of images.
+%
+% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
+%
+% The format of the ReadJP2Image method is:
+%
+% Image *ReadJP2Image(const ImageInfo *image_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+#if defined(MAGICKCORE_JP2_DELEGATE)
+
+typedef struct _StreamManager
+{
+ jas_stream_t
+ *stream;
+
+ Image
+ *image;
+} StreamManager;
+
+static int BlobRead(jas_stream_obj_t *object,char *buffer,const int length)
+{
+ ssize_t
+ count;
+
+ StreamManager
+ *source;
+
+ source=(StreamManager *) object;
+ count=ReadBlob(source->image,(size_t) length,(unsigned char *) buffer);
+ return((int) count);
+}
+
+static int BlobWrite(jas_stream_obj_t *object,char *buffer,const int length)
+{
+ ssize_t
+ count;
+
+ StreamManager
+ *source;
+
+ source=(StreamManager *) object;
+ count=WriteBlob(source->image,(size_t) length,(unsigned char *) buffer);
+ return((int) count);
+}
+
+static long BlobSeek(jas_stream_obj_t *object,long offset,int origin)
+{
+ StreamManager
+ *source;
+
+ source=(StreamManager *) object;
+ return((long) SeekBlob(source->image,offset,origin));
+}
+
+static int BlobClose(jas_stream_obj_t *object)
+{
+ StreamManager
+ *source;
+
+ source=(StreamManager *) object;
+ (void) CloseBlob(source->image);
+ source=(StreamManager *) RelinquishMagickMemory(source);
+ return(0);
+}
+
+static inline size_t MagickMax(const size_t x,const size_t y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static jas_stream_t *JP2StreamManager(Image *image)
+{
+ static jas_stream_ops_t
+ StreamOperators =
+ {
+ BlobRead,
+ BlobWrite,
+ BlobSeek,
+ BlobClose
+ };
+
+ jas_stream_t
+ *stream;
+
+ StreamManager
+ *source;
+
+ stream=(jas_stream_t *) jas_malloc(sizeof(*stream));
+ if (stream == (jas_stream_t *) NULL)
+ return((jas_stream_t *) NULL);
+ (void) ResetMagickMemory(stream,0,sizeof(*stream));
+ stream->rwlimit_=(-1);
+ stream->obj_=(jas_stream_obj_t *) jas_malloc(sizeof(StreamManager));
+ if (stream->obj_ == (jas_stream_obj_t *) NULL)
+ return((jas_stream_t *) NULL);
+ (void) ResetMagickMemory(stream->obj_,0,sizeof(StreamManager));
+ stream->ops_=(&StreamOperators);
+ stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
+ stream->bufbase_=(unsigned char *) jas_malloc(JAS_STREAM_BUFSIZE+
+ JAS_STREAM_MAXPUTBACK);
+ if (stream->bufbase_ == (void *) NULL)
+ {
+ stream->bufbase_=stream->tinybuf_;
+ stream->bufsize_=1;
+ }
+ else
+ {
+ stream->bufmode_=JAS_STREAM_FREEBUF | JAS_STREAM_BUFMODEMASK;
+ stream->bufsize_=JAS_STREAM_BUFSIZE;
+ }
+ stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
+ stream->ptr_=stream->bufstart_;
+ stream->cnt_=0;
+ source=(StreamManager *) stream->obj_;
+ source->image=image;
+ return(stream);
+}
+
+static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ long
+ components[4],
+ y;
+
+ jas_cmprof_t
+ *cm_profile;
+
+ jas_iccprof_t
+ *icc_profile;
+
+ jas_image_t
+ *jp2_image;
+
+ jas_matrix_t
+ *pixels[4];
+
+ jas_stream_t
+ *jp2_stream;
+
+ MagickBooleanType
+ status;
+
+ QuantumAny
+ range[4];
+
+ register long
+ i,
+ x;
+
+ register PixelPacket
+ *q;
+
+ unsigned long
+ maximum_component_depth,
+ number_components,
+ pixel,
+ x_step[4],
+ y_step[4];
+
+ /*
+ Open image file.
+ */
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ image=AcquireImage(image_info);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ {
+ image=DestroyImageList(image);
+ return((Image *) NULL);
+ }
+ /*
+ Initialize JPEG 2000 API.
+ */
+ jp2_stream=JP2StreamManager(image);
+ if (jp2_stream == (jas_stream_t *) NULL)
+ ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
+ jp2_image=jas_image_decode(jp2_stream,-1,0);
+ if (jp2_image == (jas_image_t *) NULL)
+ {
+ (void) jas_stream_close(jp2_stream);
+ ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
+ }
+ image->columns=jas_image_width(jp2_image);
+ image->rows=jas_image_height(jp2_image);
+ switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
+ {
+ case JAS_CLRSPC_FAM_RGB:
+ {
+ components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_R);
+ components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_G);
+ components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_B);
+ if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(CorruptImageError,"MissingImageChannel");
+ }
+ number_components=3;
+ components[3]=jas_image_getcmptbytype(jp2_image,3);
+ if (components[3] > 0)
+ {
+ image->matte=MagickTrue;
+ number_components++;
+ }
+ break;
+ }
+ case JAS_CLRSPC_FAM_GRAY:
+ {
+ components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_GRAY_Y);
+ if (components[0] < 0)
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(CorruptImageError,"MissingImageChannel");
+ }
+ number_components=1;
+ break;
+ }
+ case JAS_CLRSPC_FAM_YCBCR:
+ {
+ components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_Y);
+ components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CB);
+ components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CR);
+ if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(CorruptImageError,"MissingImageChannel");
+ }
+ number_components=3;
+ components[3]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_UNKNOWN);
+ if (components[3] > 0)
+ {
+ image->matte=MagickTrue;
+ number_components++;
+ }
+ image->colorspace=YCbCrColorspace;
+ break;
+ }
+ default:
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(CoderError,"ColorspaceModelIsNotSupported");
+ }
+ }
+ image->compression=JPEG2000Compression;
+ for (i=0; i < (long) number_components; i++)
+ {
+ unsigned long
+ height,
+ width;
+
+ width=(unsigned long) (jas_image_cmptwidth(jp2_image,components[i])*
+ jas_image_cmpthstep(jp2_image,components[i]));
+ height=(unsigned long) (jas_image_cmptheight(jp2_image,components[i])*
+ jas_image_cmptvstep(jp2_image,components[i]));
+ if ((width != image->columns) || (height != image->rows) ||
+ (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
+ (jas_image_cmpttly(jp2_image,components[i]) != 0))
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported");
+ }
+ x_step[i]=(unsigned int) jas_image_cmpthstep(jp2_image,components[i]);
+ y_step[i]=(unsigned int) jas_image_cmptvstep(jp2_image,components[i]);
+ }
+ /*
+ Convert JPEG 2000 pixels.
+ */
+ image->matte=number_components > 3 ? MagickTrue : MagickFalse;
+ if (image_info->ping != MagickFalse)
+ {
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ return(GetFirstImageInList(image));
+ }
+ maximum_component_depth=0;
+ for (i=0; i < (long) number_components; i++)
+ {
+ maximum_component_depth=(unsigned int) MagickMax((size_t)
+ jas_image_cmptprec(jp2_image,components[i]),(size_t)
+ maximum_component_depth);
+ pixels[i]=jas_matrix_create(1,(int) (image->columns/x_step[i]));
+ if (pixels[i] == (jas_matrix_t *) NULL)
+ {
+ jas_image_destroy(jp2_image);
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ }
+ if (maximum_component_depth <= 8)
+ image->depth=(unsigned long) MagickMin(MAGICKCORE_QUANTUM_DEPTH,8);
+ else
+ image->depth=(unsigned long) MagickMin(MAGICKCORE_QUANTUM_DEPTH,16);
+ for (i=0; i < (long) number_components; i++)
+ range[i]=GetQuantumRange((unsigned long) jas_image_cmptprec(jp2_image,
+ components[i]));
+ for (y=0; y < (long) image->rows; y++)
+ {
+ q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (i=0; i < (long) number_components; i++)
+ (void) jas_image_readcmpt(jp2_image,(short) components[i],0,
+ ((unsigned int) y)/y_step[i],((unsigned int) image->columns)/x_step[i],
+ 1,pixels[i]);
+ switch (number_components)
+ {
+ case 1:
+ {
+ /*
+ Grayscale.
+ */
+ for (x=0; x < (long) image->columns; x++)
+ {
+ pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
+ q->red=ScaleAnyToQuantum(pixel,range[0]);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ break;
+ }
+ case 3:
+ {
+ /*
+ RGB.
+ */
+ for (x=0; x < (long) image->columns; x++)
+ {
+ pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
+ q->red=ScaleAnyToQuantum(pixel,range[0]);
+ pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
+ q->green=ScaleAnyToQuantum(pixel,range[1]);
+ pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
+ q->blue=ScaleAnyToQuantum(pixel,range[2]);
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ /*
+ RGBA.
+ */
+ for (x=0; x < (long) image->columns; x++)
+ {
+ pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
+ q->red=ScaleAnyToQuantum(pixel,range[0]);
+ pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
+ q->green=ScaleAnyToQuantum(pixel,range[1]);
+ pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
+ q->blue=ScaleAnyToQuantum(pixel,range[2]);
+ pixel=(QuantumAny) jas_matrix_getv(pixels[3],x/x_step[3]);
+ q->opacity=ScaleAnyToQuantum(pixel,range[3]);
+ q++;
+ }
+ break;
+ }
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ status=SetImageProgress(image,LoadImageTag,y,image->rows);
+ if (status == MagickFalse)
+ break;
+ }
+ cm_profile=jas_image_cmprof(jp2_image);
+ icc_profile=(jas_iccprof_t *) NULL;
+ if (cm_profile != (jas_cmprof_t *) NULL)
+ icc_profile=jas_iccprof_createfromcmprof(cm_profile);
+ if (icc_profile != (jas_iccprof_t *) NULL)
+ {
+ jas_stream_t
+ *icc_stream;
+
+ icc_stream=jas_stream_memopen(NULL,0);
+ if ((icc_stream != (jas_stream_t *) NULL) &&
+ (jas_iccprof_save(icc_profile,icc_stream) == 0) &&
+ (jas_stream_flush(icc_stream) == 0))
+ {
+ StringInfo
+ *icc_profile,
+ *profile;
+
+ jas_stream_memobj_t
+ *blob;
+
+ /*
+ Extract the icc profile, handle errors without much noise.
+ */
+ blob=(jas_stream_memobj_t *) icc_stream->obj_;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+ "Profile: ICC, %lu bytes",(unsigned long) blob->len_);
+ profile=AcquireStringInfo(blob->len_);
+ SetStringInfoDatum(profile,blob->buf_);
+ icc_profile=(StringInfo *) GetImageProfile(image,"icc");
+ if (icc_profile == (StringInfo *) NULL)
+ (void) SetImageProfile(image,"icc",profile);
+ else
+ (void) ConcatenateStringInfo(icc_profile,profile);
+ profile=DestroyStringInfo(profile);
+ (void) jas_stream_close(icc_stream);
+ }
+ }
+ (void) jas_stream_close(jp2_stream);
+ jas_image_destroy(jp2_image);
+ for (i=0; i < (long) number_components; i++)
+ jas_matrix_destroy(pixels[i]);
+ return(GetFirstImageInList(image));
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e g i s t e r J P 2 I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RegisterJP2Image() adds attributes for the JP2 image format to the list of
+% supported formats. The attributes include the image format tag, a method
+% method to read and/or write the format, whether the format supports the
+% saving of more than one frame to the same file or blob, whether the format
+% supports native in-memory I/O, and a brief description of the format.
+%
+% The format of the RegisterJP2Image method is:
+%
+% unsigned long RegisterJP2Image(void)
+%
+*/
+ModuleExport unsigned long RegisterJP2Image(void)
+{
+ MagickInfo
+ *entry;
+
+ entry=SetMagickInfo("JP2");
+ entry->description=ConstantString("JPEG-2000 File Format Syntax");
+ entry->module=ConstantString("JP2");
+ entry->magick=(IsImageFormatHandler *) IsJP2;
+ entry->adjoin=MagickFalse;
+ entry->seekable_stream=MagickTrue;
+ entry->thread_support=NoThreadSupport;
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ entry->decoder=(DecodeImageHandler *) ReadJP2Image;
+ entry->encoder=(EncodeImageHandler *) WriteJP2Image;
+#endif
+ (void) RegisterMagickInfo(entry);
+ entry=SetMagickInfo("JPC");
+ entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
+ entry->module=ConstantString("JPC");
+ entry->magick=(IsImageFormatHandler *) IsJPC;
+ entry->adjoin=MagickFalse;
+ entry->seekable_stream=MagickTrue;
+ entry->thread_support=NoThreadSupport;
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ entry->decoder=(DecodeImageHandler *) ReadJP2Image;
+ entry->encoder=(EncodeImageHandler *) WriteJP2Image;
+#endif
+ (void) RegisterMagickInfo(entry);
+ entry=SetMagickInfo("JPX");
+ entry->description=ConstantString("JPEG-2000 File Format Syntax");
+ entry->module=ConstantString("JPX");
+ entry->magick=(IsImageFormatHandler *) IsJPC;
+ entry->adjoin=MagickFalse;
+ entry->seekable_stream=MagickTrue;
+ entry->thread_support=NoThreadSupport;
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ entry->decoder=(DecodeImageHandler *) ReadJP2Image;
+ entry->encoder=(EncodeImageHandler *) WriteJP2Image;
+#endif
+ (void) RegisterMagickInfo(entry);
+ entry=SetMagickInfo("PGX");
+ entry->description=ConstantString("JPEG-2000 VM Format");
+ entry->module=ConstantString("PGX");
+ entry->magick=(IsImageFormatHandler *) IsJPC;
+ entry->adjoin=MagickFalse;
+ entry->seekable_stream=MagickTrue;
+ entry->thread_support=NoThreadSupport;
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ entry->decoder=(DecodeImageHandler *) ReadJP2Image;
+#endif
+ (void) RegisterMagickInfo(entry);
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ jas_init();
+#endif
+ return(MagickImageCoderSignature);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n r e g i s t e r J P 2 I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnregisterJP2Image() removes format registrations made by the JP2 module
+% from the list of supported formats.
+%
+% The format of the UnregisterJP2Image method is:
+%
+% UnregisterJP2Image(void)
+%
+*/
+ModuleExport void UnregisterJP2Image(void)
+{
+ (void) UnregisterMagickInfo("JP2");
+ (void) UnregisterMagickInfo("JPC");
+ (void) UnregisterMagickInfo("PGX");
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ jas_cleanup();
+#endif
+}
+
+#if defined(MAGICKCORE_JP2_DELEGATE)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W r i t e J P 2 I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteJP2Image() writes an image in the JPEG 2000 image format.
+%
+% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
+%
+% The format of the WriteJP2Image method is:
+%
+% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
+%
+% A description of each parameter follows.
+%
+% o image_info: the image info.
+%
+% o image: The image.
+%
+*/
+static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
+{
+ char
+ *key,
+ magick[MaxTextExtent],
+ *options;
+
+ const char
+ *option;
+
+ long
+ format,
+ y;
+
+ jas_image_cmptparm_t
+ component_info[4];
+
+ jas_image_t
+ *jp2_image;
+
+ jas_matrix_t
+ *pixels[4];
+
+ jas_stream_t
+ *jp2_stream;
+
+ MagickBooleanType
+ status;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ number_components;
+
+ /*
+ Open image file.
+ */
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
+ if (status == MagickFalse)
+ return(status);
+ /*
+ Intialize JPEG 2000 API.
+ */
+ if (image->colorspace != RGBColorspace)
+ (void) TransformImageColorspace(image,RGBColorspace);
+ jp2_stream=JP2StreamManager(image);
+ if (jp2_stream == (jas_stream_t *) NULL)
+ ThrowWriterException(DelegateError,"UnableToManageJP2Stream");
+ number_components=image->matte ? 4UL : 3UL;
+ if ((image_info->type != TrueColorType) &&
+ IsGrayImage(image,&image->exception))
+ number_components=1;
+ if ((image->columns != (unsigned int) image->columns) ||
+ (image->rows != (unsigned int) image->rows))
+ ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
+ for (i=0; i < (long) number_components; i++)
+ {
+ component_info[i].tlx=0;
+ component_info[i].tly=0;
+ component_info[i].hstep=1;
+ component_info[i].vstep=1;
+ component_info[i].width=(unsigned int) image->columns;
+ component_info[i].height=(unsigned int) image->rows;
+ component_info[i].prec=(int) MagickMin(image->depth,16);
+ component_info[i].sgnd=MagickFalse;
+ }
+ jp2_image=jas_image_create((int) number_components,component_info,
+ JAS_CLRSPC_UNKNOWN);
+ if (jp2_image == (jas_image_t *) NULL)
+ ThrowWriterException(DelegateError,"UnableToCreateImage");
+ if (number_components == 1)
+ {
+ /*
+ sRGB Grayscale.
+ */
+ jas_image_setclrspc(jp2_image,JAS_CLRSPC_SGRAY);
+ jas_image_setcmpttype(jp2_image,0,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
+ }
+ else
+ {
+ /*
+ sRGB.
+ */
+ if (number_components == 4 )
+ jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY);
+ jas_image_setclrspc(jp2_image,JAS_CLRSPC_SRGB);
+ jas_image_setcmpttype(jp2_image,0,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
+ jas_image_setcmpttype(jp2_image,1,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
+ jas_image_setcmpttype(jp2_image,2,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
+ }
+ /*
+ Convert to JPEG 2000 pixels.
+ */
+ for (i=0; i < (long) number_components; i++)
+ {
+ pixels[i]=jas_matrix_create(1,(int) image->columns);
+ if (pixels[i] == (jas_matrix_t *) NULL)
+ {
+ for (x=0; x < i; x++)
+ jas_matrix_destroy(pixels[x]);
+ jas_image_destroy(jp2_image);
+ ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (number_components == 1)
+ jas_matrix_setv(pixels[0],x,ScaleQuantumToShort(
+ PixelIntensityToQuantum(p)) >> (16-MagickMin(image->depth,16)));
+ else
+ {
+ jas_matrix_setv(pixels[0],x,ScaleQuantumToShort(p->red) >> (16-
+ MagickMin(image->depth,16)));
+ jas_matrix_setv(pixels[1],x,ScaleQuantumToShort(p->green) >> (16-
+ MagickMin(image->depth,16)));
+ jas_matrix_setv(pixels[2],x,ScaleQuantumToShort(p->blue) >> (16-
+ MagickMin(image->depth,16)));
+ if (number_components > 3)
+ jas_matrix_setv(pixels[3],x,ScaleQuantumToShort((Quantum)
+ (QuantumRange-p->opacity)) >> (16-MagickMin(image->depth,16)));
+ }
+ p++;
+ }
+ for (i=0; i < (long) number_components; i++)
+ (void) jas_image_writecmpt(jp2_image,(short) i,0,(unsigned int) y,
+ (unsigned int) image->columns,1,pixels[i]);
+ status=SetImageProgress(image,SaveImageTag,y,image->rows);
+ if (status == MagickFalse)
+ break;
+ }
+ (void) CopyMagickString(magick,image_info->magick,MaxTextExtent);
+ LocaleLower(magick);
+ format=jas_image_strtofmt(magick);
+ options=(char *) NULL;
+ ResetImageOptionIterator(image_info);
+ key=GetNextImageOption(image_info);
+ while (key != (char *) NULL)
+ {
+ option=GetImageOption(image_info,key);
+ if (option != (const char *) NULL)
+ {
+ if (LocaleNCompare(key,"jp2:",4) == 0)
+ {
+ (void) ConcatenateString(&options,key+4);
+ if (*option != '\0')
+ {
+ (void) ConcatenateString(&options,"=");
+ (void) ConcatenateString(&options,option);
+ }
+ (void) ConcatenateString(&options," ");
+ }
+ }
+ key=GetNextImageOption(image_info);
+ }
+ option=GetImageOption(image_info,"jp2:rate");
+ if ((option == (const char *) NULL) &&
+ (image_info->compression != LosslessJPEGCompression) &&
+ (image->quality != UndefinedCompressionQuality) &&
+ (image->quality <= 99) && ((image->rows*image->columns) > 2500))
+ {
+ char
+ option[MaxTextExtent];
+
+ double
+ alpha,
+ header_size,
+ number_pixels,
+ rate,
+ target_size;
+
+ alpha=115.0-image->quality;
+ rate=100.0/(alpha*alpha);
+ header_size=550.0;
+ header_size+=(number_components-1)*142;
+ number_pixels=(double) image->rows*image->columns*number_components*
+ (GetImageQuantumDepth(image,MagickTrue)/8);
+ target_size=(number_pixels*rate)+header_size;
+ rate=target_size/number_pixels;
+ (void) FormatMagickString(option,MaxTextExtent,"rate=%g",rate);
+ (void) ConcatenateString(&options,option);
+ }
+ status=jas_image_encode(jp2_image,jp2_stream,format,options) != 0 ?
+ MagickTrue : MagickFalse;
+ (void) jas_stream_close(jp2_stream);
+ for (i=0; i < (long) number_components; i++)
+ jas_matrix_destroy(pixels[i]);
+ jas_image_destroy(jp2_image);
+ if (status != MagickFalse)
+ ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
+ return(MagickTrue);
+}
+#endif