]> granicus.if.org Git - imagemagick/blobdiff - coders/jp2.c
(no commit message)
[imagemagick] / coders / jp2.c
index e9b117ebef898b3eb287b4bfc719e1b5173a645d..4c6737c5b936479286cad9b1f587c719aa617ec3 100644 (file)
@@ -96,7 +96,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 +141,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);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%   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);
 }
 \f
@@ -238,7 +204,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 +214,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 +223,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 +284,9 @@ static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
   ssize_t
     y;
 
+  unsigned char
+    sans[4];
+
   /*
     Open image file.
   */
@@ -338,10 +307,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);
@@ -427,17 +402,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
@@ -572,6 +538,21 @@ ModuleExport size_t RegisterJP2Image(void)
 #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
   entry->decoder=(DecodeImageHandler *) ReadJP2Image;
   entry->encoder=(EncodeImageHandler *) WriteJP2Image;
+#endif
+  (void) RegisterMagickInfo(entry);
+  entry=SetMagickInfo("J2C");
+  entry->description=ConstantString("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;
+#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
+  entry->decoder=(DecodeImageHandler *) ReadJP2Image;
+  entry->encoder=(EncodeImageHandler *) WriteJP2Image;
 #endif
   (void) RegisterMagickInfo(entry);
   entry=SetMagickInfo("J2K");
@@ -610,7 +591,7 @@ ModuleExport size_t RegisterJP2Image(void)
     entry->version=ConstantString(version);
   entry->mime_type=ConstantString("image/jp2");
   entry->module=ConstantString("JP2");
-  entry->magick=(IsImageFormatHandler *) IsJPC;
+  entry->magick=(IsImageFormatHandler *) IsJP2;
   entry->adjoin=MagickFalse;
   entry->seekable_stream=MagickTrue;
   entry->thread_support=NoThreadSupport;
@@ -677,11 +658,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;
@@ -738,9 +791,17 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
     if (((1UL << (i+2)) > image->columns) && ((1UL << (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
@@ -763,8 +824,8 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
         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 +833,8 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
       /*
         Set quality PSNR.
       */
-      p=value;
-      for (i=1; sscanf(p,"%f",&parameters.tcp_distoratio[i]) == 1; i++)
+      p=option;
+      for (i=0; sscanf(p,"%f",&parameters.tcp_distoratio[i]) == 1; i++)
       {
         if (i > 100)
           break;
@@ -783,11 +844,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,8 +870,8 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
       /*
         Set compression rate.
       */
-      p=value;
-      for (i=1; sscanf(p,"%f",&parameters.tcp_rates[i]) == 1; i++)
+      p=option;
+      for (i=0; sscanf(p,"%f",&parameters.tcp_rates[i]) == 1; i++)
       {
         if (i > 100)
           break;
@@ -806,38 +881,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 defined(BUGINOPENJPEG)
-  if ((image->depth == 12) &&
-      ((image->columns == 2048) || (image->rows == 1080) ||
-       (image->columns == 4096) || (image->rows == 2160)))
-    {
-      /*
-        Digital Cinema profile compliance.
-      */
-      if ((image->columns == 2048) || (image->rows == 1080))
-        {
-          /*
-            Digital Cinema 2K.
-          */
-          parameters.cp_cinema=OPJ_CINEMA2K_48;
-          parameters.cp_rsiz=OPJ_CINEMA2K;
-        }
-      if ((image->columns == 4096) || (image->rows == 2160))
-        {
-          /*
-            Digital Cinema 4K.
-          */
-          parameters.cp_cinema=OPJ_CINEMA4K_24;
-          parameters.cp_rsiz=OPJ_CINEMA4K;
-        }
-    }
-#endif
-  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",
+      &parameters.subsampling_dx,&parameters.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)
@@ -847,8 +899,7 @@ 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;
@@ -857,6 +908,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
       if (image->alpha_trait == BlendPixelTrait)
         channels++;
     }
+  parameters.tcp_mct=channels == 3 ? 1 : 0;
   ResetMagickMemory(jp2_info,0,sizeof(jp2_info));
   for (i=0; i < (ssize_t) channels; i++)
   {
@@ -884,6 +936,10 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
     parameters.subsampling_dx+1;
   jp2_image->y1=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,&parameters);
   /*
     Convert to JP2 pixels.
   */
@@ -945,7 +1001,7 @@ static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
           }
         }
       }
-      p++;
+      p+=GetPixelChannels(image);
     }
     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
       image->rows);