X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=coders%2Fjp2.c;h=ba7d5a82ebc70cc2260904a974aaa21bf55ee7b2;hb=7beda643d8713ff62ffc17b57e4b1f0d3fc9c523;hp=ddc0d59a61b3840a6085c0ca82d436c7c4e9ee52;hpb=77559f47235750d41f5c378bddb6973f7d41153e;p=imagemagick diff --git a/coders/jp2.c b/coders/jp2.c index ddc0d59a6..ba7d5a82e 100644 --- a/coders/jp2.c +++ b/coders/jp2.c @@ -17,7 +17,7 @@ % June 2001 % % % % % -% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization % +% Copyright 1999-2015 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 % @@ -39,34 +39,36 @@ /* Include declarations. */ -#include "magick/studio.h" -#include "magick/artifact.h" -#include "magick/attribute.h" -#include "magick/blob.h" -#include "magick/blob-private.h" -#include "magick/cache.h" -#include "magick/colorspace.h" -#include "magick/colorspace-private.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/pixel-accessor.h" -#include "magick/profile.h" -#include "magick/quantum-private.h" -#include "magick/static.h" -#include "magick/statistic.h" -#include "magick/string_.h" -#include "magick/string-private.h" -#include "magick/module.h" +#include "MagickCore/studio.h" +#include "MagickCore/artifact.h" +#include "MagickCore/attribute.h" +#include "MagickCore/blob.h" +#include "MagickCore/blob-private.h" +#include "MagickCore/cache.h" +#include "MagickCore/colorspace.h" +#include "MagickCore/colorspace-private.h" +#include "MagickCore/color.h" +#include "MagickCore/color-private.h" +#include "MagickCore/exception.h" +#include "MagickCore/exception-private.h" +#include "MagickCore/image.h" +#include "MagickCore/image-private.h" +#include "MagickCore/list.h" +#include "MagickCore/magick.h" +#include "MagickCore/memory_.h" +#include "MagickCore/monitor.h" +#include "MagickCore/monitor-private.h" +#include "MagickCore/option.h" +#include "MagickCore/pixel-accessor.h" +#include "MagickCore/profile.h" +#include "MagickCore/property.h" +#include "MagickCore/quantum-private.h" +#include "MagickCore/semaphore.h" +#include "MagickCore/static.h" +#include "MagickCore/statistic.h" +#include "MagickCore/string_.h" +#include "MagickCore/string-private.h" +#include "MagickCore/module.h" #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) #include #endif @@ -76,7 +78,7 @@ */ #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) static MagickBooleanType - WriteJP2Image(const ImageInfo *,Image *); + WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *); #endif /* @@ -95,7 +97,7 @@ static MagickBooleanType % % The format of the IsJ2K method is: % -% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) +% MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % @@ -140,48 +142,14 @@ static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) */ static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) { - if (length < 12) + if (length < 4) return(MagickFalse); - if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0) + if (memcmp(magick,"\x0d\x0a\x87\x0a",4) == 0) return(MagickTrue); - if (memcmp(magick,"\x0d\x0a\x87\x0a",12) == 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 < 12) return(MagickFalse); if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0) return(MagickTrue); - if (memcmp(magick,"\x0d\x0a\x87\x0a",12) == 0) - return(MagickTrue); return(MagickFalse); } @@ -237,7 +205,7 @@ static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context) image=(Image *) context; count=ReadBlob(image,(ssize_t) length,(unsigned char *) buffer); if (count == 0) - return(-1); + return((OPJ_SIZE_T) -1); return((OPJ_SIZE_T) count); } @@ -247,18 +215,16 @@ static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context) *image; image=(Image *) context; - return(SeekBlob(image,offset,SEEK_SET) == 0 ? 1 : 0); + return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE); } -static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T length,void *context) +static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) { Image *image; image=(Image *) context; - if (DiscardBlobBytes(image,(size_t) length) == MagickFalse) - return(0); - return(length); + return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset); } static void JP2WarningHandler(const char *message,void *client_data) @@ -319,6 +285,9 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) ssize_t y; + unsigned char + sans[4]; + /* Open image file. */ @@ -329,7 +298,7 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) image_info->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); - image=AcquireImage(image_info); + image=AcquireImage(image_info,exception); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { @@ -339,10 +308,16 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) /* Initialize JP2 codec. */ + if (ReadBlob(image,4,sans) != 4) + { + image=DestroyImageList(image); + return((Image *) NULL); + } + (void) SeekBlob(image,SEEK_SET,0); if (LocaleCompare(image_info->magick,"JPT") == 0) jp2_codec=opj_create_decompress(OPJ_CODEC_JPT); else - if (LocaleCompare(image_info->magick,"J2K") == 0) + if (IsJ2K(sans,4) != MagickFalse) jp2_codec=opj_create_decompress(OPJ_CODEC_J2K); else jp2_codec=opj_create_decompress(OPJ_CODEC_JP2); @@ -360,57 +335,59 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) opj_destroy_codec(jp2_codec); ThrowReaderException(DelegateError,"UnableToManageJP2Stream"); } - jp2_stream=opj_stream_create(1024,1); + jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1); opj_stream_set_read_function(jp2_stream,JP2ReadHandler); opj_stream_set_write_function(jp2_stream,JP2WriteHandler); opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); - opj_stream_set_user_data(jp2_stream,image); + opj_stream_set_user_data(jp2_stream,image,NULL); opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image)); if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0) { - opj_stream_set_user_data(jp2_stream,NULL); - opj_stream_destroy_v3(jp2_stream); + opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } - if (image_info->extract != (char *) NULL) + jp2_status=1; + if ((image->columns != 0) && (image->rows != 0)) { - RectangleInfo - geometry; - /* Extract an area from the image. */ - SetGeometry(image,&geometry); - (void) ParseAbsoluteGeometry(image_info->extract,&geometry); - jp2_status=opj_set_decode_area(jp2_codec,jp2_image,geometry.x, - geometry.y,geometry.width,geometry.height); + jp2_status=opj_set_decode_area(jp2_codec,jp2_image, + (OPJ_INT32) image->extract_info.x,(OPJ_INT32) image->extract_info.y, + (OPJ_INT32) (image->extract_info.x+(ssize_t) image->columns), + (OPJ_INT32) (image->extract_info.y+(ssize_t) image->rows)); if (jp2_status == 0) { - opj_stream_set_user_data(jp2_stream,NULL); - opj_stream_destroy_v3(jp2_stream); + opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } } - if ((opj_decode(jp2_codec,jp2_stream,jp2_image) == 0) || - (opj_end_decompress(jp2_codec,jp2_stream) == 0)) + if (image_info->number_scenes != 0) + jp2_status=opj_get_decoded_tile(jp2_codec,jp2_stream,jp2_image, + (unsigned int) image_info->scene); + else + if (image->ping == MagickFalse) + { + jp2_status=opj_decode(jp2_codec,jp2_stream,jp2_image); + if (jp2_status != 0) + jp2_status=opj_end_decompress(jp2_codec,jp2_stream); + } + if (jp2_status == 0) { - opj_stream_set_user_data(jp2_stream,NULL); - opj_stream_destroy_v3(jp2_stream); + opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } - opj_stream_set_user_data(jp2_stream,NULL); - opj_stream_destroy_v3(jp2_stream); + opj_stream_destroy(jp2_stream); for (i=0; i < (ssize_t) jp2_image->numcomps; i++) { if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0)) { - opj_stream_set_user_data(jp2_stream,NULL); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported") @@ -421,27 +398,22 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) */ image->columns=(size_t) jp2_image->comps[0].w; image->rows=(size_t) jp2_image->comps[0].h; + image->depth=jp2_image->comps[0].prec; + status=SetImageExtent(image,image->columns,image->rows,exception); + if (status == MagickFalse) + return(DestroyImageList(image)); image->compression=JPEG2000Compression; if (jp2_image->numcomps <= 2) { - SetImageColorspace(image,GRAYColorspace); + SetImageColorspace(image,GRAYColorspace,exception); if (jp2_image->numcomps > 1) - image->matte=MagickTrue; + image->alpha_trait=BlendPixelTrait; } if (jp2_image->numcomps > 3) - image->matte=MagickTrue; + image->alpha_trait=BlendPixelTrait; for (i=0; i < (ssize_t) jp2_image->numcomps; i++) - { - if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0)) - { - opj_stream_set_user_data(jp2_stream,NULL); - opj_destroy_codec(jp2_codec); - opj_image_destroy(jp2_image); - ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported") - } if ((jp2_image->comps[i].dx > 1) || (jp2_image->comps[i].dy > 1)) - image->colorspace=YUVColorspace; - } + SetImageColorspace(image,YUVColorspace,exception); if (jp2_image->icc_profile_buf != (unsigned char *) NULL) { StringInfo @@ -450,18 +422,25 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) profile=BlobToStringInfo(jp2_image->icc_profile_buf, jp2_image->icc_profile_len); if (profile != (StringInfo *) NULL) - SetImageProfile(image,"icc",profile); + SetImageProfile(image,"icc",profile,exception); + } + if (image->ping != MagickFalse) + { + opj_destroy_codec(jp2_codec); + opj_image_destroy(jp2_image); + opj_destroy_cstr_index(&codestream_index); + return(GetFirstImageInList(image)); } for (y=0; y < (ssize_t) image->rows; y++) { - register PixelPacket + register Quantum *restrict q; register ssize_t x; q=GetAuthenticPixels(image,0,y,image->columns,1,exception); - if (q == (PixelPacket *) NULL) + if (q == (Quantum *) NULL) break; for (x=0; x < (ssize_t) image->columns; x++) { @@ -553,56 +532,83 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) */ ModuleExport size_t RegisterJP2Image(void) { + char + version[MagickPathExtent]; + MagickInfo *entry; - entry=SetMagickInfo("JP2"); - entry->description=ConstantString("JPEG-2000 File Format Syntax"); + *version='\0'; +#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) + (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version()); +#endif + entry=AcquireMagickInfo("JP2","JP2","JPEG-2000 File Format Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); - entry->module=ConstantString("JP2"); entry->magick=(IsImageFormatHandler *) IsJP2; - entry->adjoin=MagickFalse; - entry->seekable_stream=MagickTrue; - entry->thread_support=NoThreadSupport; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; +#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) + entry->decoder=(DecodeImageHandler *) ReadJP2Image; + entry->encoder=(EncodeImageHandler *) WriteJP2Image; +#endif + (void) RegisterMagickInfo(entry); + entry=AcquireMagickInfo("JP2","J2C","JPEG-2000 Code Stream Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); + entry->mime_type=ConstantString("image/jp2"); + entry->magick=(IsImageFormatHandler *) IsJ2K; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); - entry=SetMagickInfo("J2K"); - entry->description=ConstantString("JPEG-2000 Code Stream Syntax"); + entry=AcquireMagickInfo("JP2","J2K","JPEG-2000 Code Stream Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); - entry->module=ConstantString("JP2"); entry->magick=(IsImageFormatHandler *) IsJ2K; - entry->adjoin=MagickFalse; - entry->seekable_stream=MagickTrue; - entry->thread_support=NoThreadSupport; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); - entry=SetMagickInfo("JPT"); - entry->description=ConstantString("JPEG-2000 File Format Syntax"); + entry=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); - entry->module=ConstantString("JP2"); entry->magick=(IsImageFormatHandler *) IsJP2; - entry->adjoin=MagickFalse; - entry->seekable_stream=MagickTrue; - entry->thread_support=NoThreadSupport; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_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=AcquireMagickInfo("JP2","JPT","JPEG-2000 File Format Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); - entry->module=ConstantString("JP2"); - entry->magick=(IsImageFormatHandler *) IsJPC; - entry->adjoin=MagickFalse; - entry->seekable_stream=MagickTrue; - entry->thread_support=NoThreadSupport; + entry->magick=(IsImageFormatHandler *) IsJP2; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; +#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) + entry->decoder=(DecodeImageHandler *) ReadJP2Image; + entry->encoder=(EncodeImageHandler *) WriteJP2Image; +#endif + (void) RegisterMagickInfo(entry); + entry=AcquireMagickInfo("JP2","JPC","JPEG-2000 Code Stream Syntax"); + if (*version != '\0') + entry->version=ConstantString(version); + entry->mime_type=ConstantString("image/jp2"); + entry->magick=(IsImageFormatHandler *) IsJP2; + entry->flags^=CoderAdjoinFlag; + entry->flags|=CoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; @@ -634,6 +640,7 @@ ModuleExport void UnregisterJP2Image(void) { (void) UnregisterMagickInfo("JPC"); (void) UnregisterMagickInfo("JPT"); + (void) UnregisterMagickInfo("JPM"); (void) UnregisterMagickInfo("JP2"); (void) UnregisterMagickInfo("J2K"); } @@ -656,7 +663,8 @@ ModuleExport void UnregisterJP2Image(void) % % The format of the WriteJP2Image method is: % -% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image) +% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, +% ExceptionInfo *exception) % % A description of each parameter follows. % @@ -665,11 +673,117 @@ ModuleExport void UnregisterJP2Image(void) % o image: The image. % */ -static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image) + +static void CinemaProfileCompliance(const opj_image_t *jp2_image, + opj_cparameters_t *parameters) { + /* + Digital Cinema 4K profile compliant codestream. + */ + parameters->tile_size_on=OPJ_FALSE; + parameters->cp_tdx=1; + parameters->cp_tdy=1; + parameters->tp_flag='C'; + parameters->tp_on=1; + parameters->cp_tx0=0; + parameters->cp_ty0=0; + parameters->image_offset_x0=0; + parameters->image_offset_y0=0; + parameters->cblockw_init=32; + parameters->cblockh_init=32; + parameters->csty|=0x01; + parameters->prog_order=OPJ_CPRL; + parameters->roi_compno=(-1); + parameters->subsampling_dx=1; + parameters->subsampling_dy=1; + parameters->irreversible=1; + if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080)) + { + /* + Digital Cinema 2K. + */ + parameters->cp_cinema=OPJ_CINEMA2K_24; + parameters->cp_rsiz=OPJ_CINEMA2K; + parameters->max_comp_size=1041666; + if (parameters->numresolution > 6) + parameters->numresolution=6; + + } + if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160)) + { + /* + Digital Cinema 4K. + */ + parameters->cp_cinema=OPJ_CINEMA4K_24; + parameters->cp_rsiz=OPJ_CINEMA4K; + parameters->max_comp_size=1041666; + if (parameters->numresolution < 1) + parameters->numresolution=1; + if (parameters->numresolution > 7) + parameters->numresolution=7; + parameters->numpocs=2; + parameters->POC[0].tile=1; + parameters->POC[0].resno0=0; + parameters->POC[0].compno0=0; + parameters->POC[0].layno1=1; + parameters->POC[0].resno1=parameters->numresolution-1; + parameters->POC[0].compno1=3; + parameters->POC[0].prg1=OPJ_CPRL; + parameters->POC[1].tile=1; + parameters->POC[1].resno0=parameters->numresolution-1; + parameters->POC[1].compno0=0; + parameters->POC[1].layno1=1; + parameters->POC[1].resno1=parameters->numresolution; + parameters->POC[1].compno1=3; + parameters->POC[1].prg1=OPJ_CPRL; + } + parameters->tcp_numlayers=1; + parameters->tcp_rates[0]=((float) (jp2_image->numcomps*jp2_image->comps[0].w* + jp2_image->comps[0].h*jp2_image->comps[0].prec))/(parameters->max_comp_size* + 8*jp2_image->comps[0].dx*jp2_image->comps[0].dy); + parameters->cp_disto_alloc=1; +} + +static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, + ExceptionInfo *exception) +{ + const char + *option, + *property; + + int + jp2_status; + MagickBooleanType status; + opj_codec_t + *jp2_codec; + + OPJ_COLOR_SPACE + jp2_colorspace; + + opj_cparameters_t + parameters; + + opj_image_cmptparm_t + jp2_info[5]; + + opj_image_t + *jp2_image; + + opj_stream_t + *jp2_stream; + + register ssize_t + i; + + ssize_t + y; + + size_t + channels; + /* Open image file. */ @@ -679,16 +793,277 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image) assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); + assert(exception != (ExceptionInfo *) NULL); + assert(exception->signature == MagickSignature); + status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); /* Initialize JPEG 2000 API. */ - if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) - (void) TransformImageColorspace(image,sRGBColorspace); - if (status != MagickFalse) + opj_set_default_encoder_parameters(¶meters); + for (i=1; i < 6; i++) + if (((1U << (i+2)) > image->columns) && ((1U << (i+2)) > image->rows)) + break; + parameters.numresolution=i; + option=GetImageOption(image_info,"jp2:number-resolutions"); + if (option != (const char *) NULL) + parameters.numresolution=StringToInteger(option); + parameters.tcp_numlayers=1; + parameters.tcp_rates[0]=0; /* lossless */ + parameters.cp_disto_alloc=1; + if (image->quality != 0) + { + parameters.tcp_distoratio[0]=(double) image->quality; + parameters.cp_fixed_quality=OPJ_TRUE; + } + if (image_info->extract != (char *) NULL) + { + RectangleInfo + geometry; + + int + flags; + + /* + Set tile size. + */ + flags=ParseAbsoluteGeometry(image_info->extract,&geometry); + parameters.cp_tdx=(int) geometry.width; + parameters.cp_tdy=(int) geometry.width; + if ((flags & HeightValue) != 0) + parameters.cp_tdy=(int) geometry.height; + if ((flags & XValue) != 0) + parameters.cp_tx0=geometry.x; + if ((flags & YValue) != 0) + parameters.cp_ty0=geometry.y; + parameters.tile_size_on=OPJ_TRUE; + } + option=GetImageOption(image_info,"jp2:quality"); + if (option != (const char *) NULL) + { + register const char + *p; + + /* + Set quality PSNR. + */ + p=option; + for (i=0; sscanf(p,"%f",¶meters.tcp_distoratio[i]) == 1; i++) + { + if (i > 100) + break; + while ((*p != '\0') && (*p != ',')) + p++; + if (*p == '\0') + break; + p++; + } + parameters.tcp_numlayers=i+1; + parameters.cp_fixed_quality=OPJ_TRUE; + } + option=GetImageOption(image_info,"jp2:progression-order"); + if (option != (const char *) NULL) + { + if (LocaleCompare(option,"LRCP") == 0) + parameters.prog_order=OPJ_LRCP; + if (LocaleCompare(option,"RLCP") == 0) + parameters.prog_order=OPJ_RLCP; + if (LocaleCompare(option,"RPCL") == 0) + parameters.prog_order=OPJ_RPCL; + if (LocaleCompare(option,"PCRL") == 0) + parameters.prog_order=OPJ_PCRL; + if (LocaleCompare(option,"CPRL") == 0) + parameters.prog_order=OPJ_CPRL; + } + option=GetImageOption(image_info,"jp2:rate"); + if (option != (const char *) NULL) + { + register const char + *p; + + /* + Set compression rate. + */ + p=option; + for (i=0; sscanf(p,"%f",¶meters.tcp_rates[i]) == 1; i++) + { + if (i >= 100) + break; + while ((*p != '\0') && (*p != ',')) + p++; + if (*p == '\0') + break; + p++; + } + parameters.tcp_numlayers=i+1; + parameters.cp_disto_alloc=OPJ_TRUE; + } + if (image_info->sampling_factor != (const char *) NULL) + (void) sscanf(image_info->sampling_factor,"%d,%d", + ¶meters.subsampling_dx,¶meters.subsampling_dy); + property=GetImageProperty(image,"comment",exception); + if (property != (const char *) NULL) + parameters.cp_comment=ConstantString(property); + channels=3; + jp2_colorspace=OPJ_CLRSPC_SRGB; + if (image->colorspace == YUVColorspace) + { + jp2_colorspace=OPJ_CLRSPC_SYCC; + parameters.subsampling_dx=2; + } + else + { + (void) TransformImageColorspace(image,sRGBColorspace,exception); + if (IsGrayColorspace(image->colorspace) != MagickFalse) + { + channels=1; + jp2_colorspace=OPJ_CLRSPC_GRAY; + } + if (image->alpha_trait != UndefinedPixelTrait) + channels++; + } + parameters.tcp_mct=channels == 3 ? 1 : 0; + ResetMagickMemory(jp2_info,0,sizeof(jp2_info)); + for (i=0; i < (ssize_t) channels; i++) + { + jp2_info[i].prec=(OPJ_UINT32) image->depth; + jp2_info[i].bpp=(OPJ_UINT32) image->depth; + if ((image->depth == 1) && + ((LocaleCompare(image_info->magick,"JPT") == 0) || + (LocaleCompare(image_info->magick,"JP2") == 0))) + { + jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */ + jp2_info[i].bpp++; + } + jp2_info[i].sgnd=0; + jp2_info[i].dx=parameters.subsampling_dx; + jp2_info[i].dy=parameters.subsampling_dy; + jp2_info[i].w=(OPJ_UINT32) image->columns; + jp2_info[i].h=(OPJ_UINT32) image->rows; + } + jp2_image=opj_image_create((OPJ_UINT32) channels,jp2_info,jp2_colorspace); + if (jp2_image == (opj_image_t *) NULL) ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); + jp2_image->x0=parameters.image_offset_x0; + jp2_image->y0=parameters.image_offset_y0; + jp2_image->x1=(unsigned int) (2*parameters.image_offset_x0+(image->columns-1)* + parameters.subsampling_dx+1); + jp2_image->y1=(unsigned int) (2*parameters.image_offset_y0+(image->rows-1)* + parameters.subsampling_dx+1); + if ((image->depth == 12) && + ((image->columns == 2048) || (image->rows == 1080) || + (image->columns == 4096) || (image->rows == 2160))) + CinemaProfileCompliance(jp2_image,¶meters); + if (channels == 4) + jp2_image->comps[3].alpha=1; + else + if ((channels == 2) && (jp2_colorspace == OPJ_CLRSPC_GRAY)) + jp2_image->comps[1].alpha=1; + /* + Convert to JP2 pixels. + */ + for (y=0; y < (ssize_t) image->rows; y++) + { + register const Quantum + *p; + + ssize_t + x; + + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) + break; + for (x=0; x < (ssize_t) image->columns; x++) + { + for (i=0; i < (ssize_t) channels; i++) + { + double + scale; + + register int + *q; + + scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange; + q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy* + image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx); + switch (i) + { + case 0: + { + if (jp2_colorspace == OPJ_CLRSPC_GRAY) + { + *q=(int) (scale*GetPixelLuma(image,p)); + break; + } + *q=(int) (scale*GetPixelRed(image,p)); + break; + } + case 1: + { + if (jp2_colorspace == OPJ_CLRSPC_GRAY) + { + *q=(int) (scale*GetPixelAlpha(image,p)); + break; + } + *q=(int) (scale*GetPixelGreen(image,p)); + break; + } + case 2: + { + *q=(int) (scale*GetPixelBlue(image,p)); + break; + } + case 3: + { + *q=(int) (scale*GetPixelAlpha(image,p)); + break; + } + } + } + p+=GetPixelChannels(image); + } + status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, + image->rows); + if (status == MagickFalse) + break; + } + if (LocaleCompare(image_info->magick,"JPT") == 0) + jp2_codec=opj_create_compress(OPJ_CODEC_JPT); + else + if (LocaleCompare(image_info->magick,"J2K") == 0) + jp2_codec=opj_create_compress(OPJ_CODEC_J2K); + else + jp2_codec=opj_create_compress(OPJ_CODEC_JP2); + opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); + opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); + opj_setup_encoder(jp2_codec,¶meters,jp2_image); + jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE); + opj_stream_set_read_function(jp2_stream,JP2ReadHandler); + opj_stream_set_write_function(jp2_stream,JP2WriteHandler); + opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); + opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); + opj_stream_set_user_data(jp2_stream,image,NULL); + if (jp2_stream == (opj_stream_t *) NULL) + ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); + jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream); + if (jp2_status == 0) + ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); + if ((opj_encode(jp2_codec,jp2_stream) == 0) || + (opj_end_compress(jp2_codec,jp2_stream) == 0)) + { + opj_stream_destroy(jp2_stream); + opj_destroy_codec(jp2_codec); + opj_image_destroy(jp2_image); + ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); + } + /* + Free resources. + */ + opj_stream_destroy(jp2_stream); + opj_destroy_codec(jp2_codec); + opj_image_destroy(jp2_image); + (void) CloseBlob(image); return(MagickTrue); } #endif