X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=coders%2Fjp2.c;h=ba7d5a82ebc70cc2260904a974aaa21bf55ee7b2;hb=7beda643d8713ff62ffc17b57e4b1f0d3fc9c523;hp=501ef6c4e49e9b8187d016354d3c116fb650a55e;hpb=3b226a1631433c22487a5f7a5afb918b96170672;p=imagemagick diff --git a/coders/jp2.c b/coders/jp2.c index 501ef6c4e..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 % @@ -63,6 +63,7 @@ #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" @@ -96,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: % @@ -141,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); } @@ -238,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); } @@ -248,7 +215,7 @@ static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context) *image; image=(Image *) context; - return(SeekBlob(image,offset,SEEK_SET) < 0 ? 0 : 1); + return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE); } static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) @@ -257,7 +224,7 @@ static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) *image; image=(Image *) context; - return(SeekBlob(image,offset,SEEK_CUR) < 0 ? 0 : offset); + return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset); } static void JP2WarningHandler(const char *message,void *client_data) @@ -318,6 +285,9 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) ssize_t y; + unsigned char + sans[4]; + /* Open image file. */ @@ -338,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); @@ -364,48 +340,54 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) 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"); } + jp2_status=1; if ((image->columns != 0) && (image->rows != 0)) { /* Extract an area from the image. */ - jp2_status=opj_set_decode_area(jp2_codec,jp2_image,image->extract_info.x, - image->extract_info.y,image->extract_info.x+image->columns, - image->extract_info.y+image->rows); + 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") @@ -417,6 +399,9 @@ 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) { @@ -427,17 +412,8 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) if (jp2_image->numcomps > 3) 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 @@ -448,6 +424,13 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) if (profile != (StringInfo *) NULL) 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 Quantum @@ -550,70 +533,82 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) ModuleExport size_t RegisterJP2Image(void) { char - version[MaxTextExtent]; + version[MagickPathExtent]; MagickInfo *entry; *version='\0'; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) - (void) FormatLocaleString(version,MaxTextExtent,"%s",opj_version()); + (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version()); #endif - entry=SetMagickInfo("JP2"); - entry->description=ConstantString("JPEG-2000 File Format Syntax"); + 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=SetMagickInfo("J2K"); - entry->description=ConstantString("JPEG-2000 Code Stream Syntax"); + entry=AcquireMagickInfo("JP2","J2C","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=AcquireMagickInfo("JP2","J2K","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=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format 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; #endif (void) RegisterMagickInfo(entry); - entry=SetMagickInfo("JPT"); - entry->description=ConstantString("JPEG-2000 File Format 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 *) 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","JPC","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 *) 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; @@ -645,6 +640,7 @@ ModuleExport void UnregisterJP2Image(void) { (void) UnregisterMagickInfo("JPC"); (void) UnregisterMagickInfo("JPT"); + (void) UnregisterMagickInfo("JPM"); (void) UnregisterMagickInfo("JP2"); (void) UnregisterMagickInfo("J2K"); } @@ -677,11 +673,83 @@ ModuleExport void UnregisterJP2Image(void) % o image: The 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 - *value; + *option, + *property; int jp2_status; @@ -735,12 +803,20 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, */ opj_set_default_encoder_parameters(¶meters); for (i=1; i < 6; i++) - if (((1UL << (i+2)) > image->columns) && ((1UL << (i+2)) > image->rows)) + 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_distoratio[0]=(double) image->quality; - parameters.cp_fixed_quality=OPJ_TRUE; + 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 @@ -753,18 +829,18 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, Set tile size. */ flags=ParseAbsoluteGeometry(image_info->extract,&geometry); - parameters.cp_tdx=geometry.width; - parameters.cp_tdy=geometry.width; + parameters.cp_tdx=(int) geometry.width; + parameters.cp_tdy=(int) geometry.width; if ((flags & HeightValue) != 0) - parameters.cp_tdy=geometry.height; + 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; } - value=GetImageArtifact(image,"jp2:quality"); - if (value != (const char *) NULL) + option=GetImageOption(image_info,"jp2:quality"); + if (option != (const char *) NULL) { register const char *p; @@ -772,8 +848,8 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, /* Set quality PSNR. */ - p=value; - for (i=1; sscanf(p,"%f",¶meters.tcp_distoratio[i]) == 1; i++) + p=option; + for (i=0; sscanf(p,"%f",¶meters.tcp_distoratio[i]) == 1; i++) { if (i > 100) break; @@ -783,11 +859,25 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, break; p++; } - parameters.tcp_numlayers=i; + parameters.tcp_numlayers=i+1; parameters.cp_fixed_quality=OPJ_TRUE; } - value=GetImageArtifact(image,"jp2:rate"); - if (value != (const char *) NULL) + 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; @@ -795,10 +885,10 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, /* Set compression rate. */ - p=value; - for (i=1; sscanf(p,"%f",¶meters.tcp_rates[i]) == 1; i++) + p=option; + for (i=0; sscanf(p,"%f",¶meters.tcp_rates[i]) == 1; i++) { - if (i > 100) + if (i >= 100) break; while ((*p != '\0') && (*p != ',')) p++; @@ -806,31 +896,15 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, break; p++; } - parameters.tcp_numlayers=i; + parameters.tcp_numlayers=i+1; parameters.cp_disto_alloc=OPJ_TRUE; } - if (image->depth == 12) - { - if ((image->columns == 2048) || (image->rows == 1080)) - { - /* - Digital Cinema 2K profile compliance. - */ - parameters.cp_cinema=OPJ_CINEMA2K_48; - parameters.cp_rsiz=OPJ_CINEMA2K; - } - if ((image->columns == 4096) || (image->rows == 2160)) - { - /* - Digital Cinema 4K profile compliance. - */ - parameters.cp_cinema=OPJ_CINEMA4K_24; - parameters.cp_rsiz=OPJ_CINEMA4K; - } - } - value=GetImageProperty(image,"comment",exception); - if (value != (const char *) NULL) - parameters.cp_comment=ConstantString(value); + 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) @@ -840,21 +914,21 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, } else { - if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) - (void) TransformImageColorspace(image,sRGBColorspace,exception); + (void) TransformImageColorspace(image,sRGBColorspace,exception); if (IsGrayColorspace(image->colorspace) != MagickFalse) { channels=1; jp2_colorspace=OPJ_CLRSPC_GRAY; } - if (image->alpha_trait == BlendPixelTrait) + 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=image->depth; - jp2_info[i].bpp=image->depth; + 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))) @@ -865,18 +939,27 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, jp2_info[i].sgnd=0; jp2_info[i].dx=parameters.subsampling_dx; jp2_info[i].dy=parameters.subsampling_dy; - jp2_info[i].w=image->columns; - jp2_info[i].h=image->rows; + jp2_info[i].w=(OPJ_UINT32) image->columns; + jp2_info[i].h=(OPJ_UINT32) image->rows; } - jp2_image=opj_image_create(channels,jp2_info,jp2_colorspace); + 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=2*parameters.image_offset_x0+(image->columns-1)* - parameters.subsampling_dx+1; - jp2_image->y1=2*parameters.image_offset_y0+(image->rows-1)* - parameters.subsampling_dx+1; + 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. */ @@ -938,7 +1021,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, } } } - p++; + p+=GetPixelChannels(image); } status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, image->rows); @@ -960,7 +1043,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, 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); if (jp2_stream == (opj_stream_t *) NULL) ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream); @@ -969,8 +1052,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, if ((opj_encode(jp2_codec,jp2_stream) == 0) || (opj_end_compress(jp2_codec,jp2_stream) == 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); ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); @@ -978,8 +1060,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, /* Free resources. */ - 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); (void) CloseBlob(image);