示例#1
0
ICCProfile JP2Instance::ReadICCProfile()
{
   CheckOpenStream( !m_path.IsEmpty() && s_jasperInitialized, "ReadICCProfile" );

   if ( jas_image_cmprof( m_jp2Image ) == nullptr )
      return ICCProfile();

   jas_stream_t* iccStream = nullptr;
   try
   {
      // Create a growable memory stream for output
      iccStream = jas_stream_memopen( 0, 0 );

      if ( iccStream == nullptr )
         JP2KERROR( "Extracting ICC profile from JPEG2000 image: Unable to create JasPer stream" );

      if ( jas_iccprof_save( jas_image_cmprof( m_jp2Image )->iccprof, iccStream ) < 0 )
         JP2KERROR( "Extracting ICC profile from JPEG2000 image: Error saving profile to JasPer stream" );

      long iccSize = jas_stream_tell( iccStream );

      if ( iccSize <= 0 )
         JP2KERROR( "Extracting ICC profile from JPEG2000 image: Invalid JasPer stream position" );

      ByteArray iccData( iccSize );

      jas_stream_rewind( iccStream );

      if ( jas_stream_read( iccStream, iccData.Begin(), iccSize ) != iccSize )
         JP2KERROR( "Extracting ICC profile from JPEG2000 image: Error reading JasPer stream" );

      jas_stream_close( iccStream ), iccStream = nullptr;

      ICCProfile icc( iccData );
      if ( icc.IsProfile() )
         Console().WriteLn( "<end><cbr>ICC profile extracted: \'" + icc.Description() + "\', " + String( icc.ProfileSize() ) + " bytes." );
      return icc;
   }
   catch ( ... )
   {
      if ( iccStream != nullptr )
         jas_stream_close( iccStream );
      Close();
      throw;
   }
}
示例#2
0
文件: JPEG.cpp 项目: SunGong1993/PCL
template <class P> inline
static void WriteJPEGImage( const GenericImage<P>& img, JPEGWriter& writer,
                            const ICCProfile& icc, JPEGFileData* fileData )
{
   if ( !writer.IsOpen() )
      throw JPEG::WriterNotInitialized( String() );

   if ( writer.Options().lowerRange >= writer.Options().upperRange )
      throw JPEG::InvalidNormalizationRange( writer.Path() );

   if ( img.IsEmptySelection() )
      throw JPEG::InvalidPixelSelection( writer.Path() );

   // Retrieve information on selected subimage.
   Rect r = img.SelectedRectangle();
   int width = r.Width();
   int height = r.Height();
   int channels = img.NumberOfSelectedChannels();

   // JPEG doesn't support alpha channels, just plain grayscale and RGB images.
   if ( channels != 1 && channels != 3 )
      throw JPEG::InvalidChannelSelection( writer.Path() );

   unsigned char qualityMarker[] =
   { 'J', 'P', 'E', 'G', 'Q', 'u', 'a', 'l', 'i', 't', 'y', 0, 0 };

   try
   {
      // Make safe copies of critical parameters.
      fileData->lowerRange = writer.Options().lowerRange;
      fileData->upperRange = writer.Options().upperRange;

      // Initialize status callback.
      if ( img.Status().IsInitializationEnabled() )
         img.Status().Initialize( String().Format(
                  "Compressing JPEG: %d channel(s), %dx%d pixels",
                  channels, width, height ), img.NumberOfSelectedSamples() );

      //
      // Allocate and initialize a JPEG compressor instance.
      //

      // This struct contains the JPEG compression parameters and pointers to
      // working space, which is allocated as needed by the IJG library.
      fileData->compressor = new ::jpeg_compress_struct;

      // Set up normal JPEG error routines, passing a pointer to our custom
      // error handler structure.
      jpeg_compressor->err = ::jpeg_std_error(
         (::jpeg_error_mgr*)(fileData->errorManager = new JPEGErrorManager( writer.Path() )) );

      // Override error_exit. JPEG library errors will be handled by throwing
      // exceptions from the JPEGErrorExit() routine.
      ((JPEGErrorManager*)fileData->errorManager)->error.error_exit = JPEGErrorExit;

      // Initialize the JPEG compression object.
      ::jpeg_create_compress( jpeg_compressor );

      //
      // Set parameters for compression.
      //

      jpeg_compressor->image_width = width;
      jpeg_compressor->image_height = height;
      jpeg_compressor->input_components = channels;
      jpeg_compressor->in_color_space = (channels == 1) ? JCS_GRAYSCALE : JCS_RGB;

      // Set default compression parameters.
      ::jpeg_set_defaults( jpeg_compressor );

      // Set any non-default parameters.

      if ( writer.JPEGOptions().quality == JPEGQuality::Unknown )
         const_cast<JPEGImageOptions&>( writer.JPEGOptions() ).quality = JPEGQuality::Default;

      // Set up compression according to specified JPEG quality.
      // We limit to baseline-JPEG values (TRUE arg in jpeg_set_quality).
      ::jpeg_set_quality( jpeg_compressor, writer.JPEGOptions().quality, TRUE );

      jpeg_compressor->optimize_coding = boolean( writer.JPEGOptions().optimizedCoding );
      jpeg_compressor->arith_code = boolean( writer.JPEGOptions().arithmeticCoding );
      jpeg_compressor->dct_method = JDCT_FLOAT;
      jpeg_compressor->write_JFIF_header = boolean( writer.JPEGOptions().JFIFMarker );
      jpeg_compressor->JFIF_major_version = writer.JPEGOptions().JFIFMajorVersion;
      jpeg_compressor->JFIF_minor_version = writer.JPEGOptions().JFIFMinorVersion;
      jpeg_compressor->density_unit = writer.Options().metricResolution ? 2 : 1;
      jpeg_compressor->X_density = RoundI( writer.Options().xResolution );
      jpeg_compressor->Y_density = RoundI( writer.Options().yResolution );

      if ( writer.JPEGOptions().progressive )
         ::jpeg_simple_progression( jpeg_compressor );

      IsoString path8 =
#ifdef __PCL_WINDOWS
         File::UnixPathToWindows( writer.Path() ).ToMBS();
#else
         writer.Path().ToUTF8();
#endif
      fileData->handle = ::fopen( path8.c_str(), "wb" );

      if ( fileData->handle == 0 )
         throw JPEG::UnableToCreateFile( writer.Path() );

      // Specify data destination (e.g., a file).
      ::jpeg_stdio_dest( jpeg_compressor, fileData->handle );

      // Start compressor.
      ::jpeg_start_compress( jpeg_compressor, TRUE );

      // Output ICC Profile data.
      // We use APP2 markers, as stated on ICC.1:2001-12.

      if ( writer.Options().embedICCProfile && icc.IsProfile() )
      {
         // Get ICC profile size in bytes from the ICC profile header
         uint32 byteCount = uint32( icc.ProfileSize() );

         if ( byteCount != 0 )
         {
            // The ICC profile must be writen as a sequence of chunks if its
            // size is larger than (roughly) 64K.

            // Number of whole chunks.
            int chunkCount = byteCount/0xFF00;

            // Remainder chunk size in bytes.
            int lastChunk = byteCount - chunkCount*0xFF00;

            // chunkCount includes a possible remainder from now on.
            if ( lastChunk != 0 )
               ++chunkCount;

            // Allocate a working buffer. 64K bytes to hold chunk data, ICC
            // marker id, etc.
            ByteArray iccData( 0xFFFF );

            // ICC JFIF marker identifier, as recommended by ICC.1:2001-12.
            // Note that this is a null-terminated string, thus the total
            // length is 12 bytes.
            ::memcpy( iccData.Begin(), "ICC_PROFILE", 12 );

            // Byte #12, next to id's null terminator, is a 1-based chunk index.
            // Byte #13 is the total count of chunks (including remainder).
            iccData[13] = uint8( chunkCount );

            // Write sequence of markers

            for ( int i = 1, offset = 0; i <= chunkCount; ++i )
            {
               // Size in bytes of this chunk.
               int size = (i == chunkCount) ? lastChunk : 0xFF00;

               // Copy ICC profile data for this chunk, preserve ICC id, etc.
               ::memcpy( iccData.At( 14 ), icc.ProfileData().At( offset ), size );

               // Keep track of this chunk's index number, one-based.
               iccData[12] = uint8( i );

               // Output an APP2 marker for this chunk.
               ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+2, (const JOCTET *)iccData.Begin(), size+14 );

               // Prepare for next chunk.
               offset += size;
            }
         }
      }

      /*
      // Output IPTC PhotoInfo data.
      // We use an APP13 marker, as recommended by IPTC.

      if ( iptc != nullptr )
      {
         size_type byteCount = iptc->MakeInfo( iptcPureData );

         if ( byteCount != 0 )
         {
            iptcData = new char[ byteCount+14 ];
            ::strcpy( (char*)iptcData, "IPTCPhotoInfo" );
            ::memcpy( ((char*)iptcData)+14, iptcPureData, byteCount );
            delete (uint8*)iptcPureData, iptcPureData = nullptr;

            ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+13,
                  (const JOCTET *)iptcData, unsigned( byteCount+14 ) );

            delete (uint8*)iptcData, iptcData = nullptr;
         }
      }
      */

      // Output JPEGQuality marker.
      // We use an APP15 marker for quality.

      qualityMarker[12] = writer.JPEGOptions().quality;
      ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+15, qualityMarker, 13 );

      //
      // Write pixels row by row.
      //

      // JSAMPLEs per row in image_buffer.
      int row_stride = width * channels;

      // Make a one-row-high sample array.
      Array<JSAMPLE> buffer( row_stride );

      // JPEG organization is chunky; PCL images are planar.
      Array<const typename P::sample*> v( channels );

      // Either rescaling or truncating to the [0,1] range is mandatory when
      // writing floating-point images to JPEG files.
      bool rescale = P::IsFloatSample() && (fileData->upperRange != 1 || fileData->lowerRange != 0);
      double k = 1.0;
      if ( rescale )
         k /= fileData->upperRange - fileData->lowerRange;

      while ( jpeg_compressor->next_scanline < jpeg_compressor->image_height )
      {
         JSAMPLE* b = buffer.Begin();

         for ( int c = 0; c < channels; ++c )
            v[c] = img.PixelAddress( r.x0,
                                     r.y0+jpeg_compressor->next_scanline,
                                     img.FirstSelectedChannel()+c );

         for ( int i = 0; i < width; ++i )
            for ( int c = 0; c < channels; ++c, ++b )
            {
               typename P::sample f = *v[c]++;

               if ( P::IsFloatSample() )
               {
                  f = typename P::sample( Range( double( f ), fileData->lowerRange, fileData->upperRange ) );
                  if ( rescale )
                     f = typename P::sample( k*(f - fileData->lowerRange) );
               }

               P::FromSample( *b, f );
            }

         b = buffer.Begin();
         ::jpeg_write_scanlines( jpeg_compressor, &b, 1 );

         img.Status() += width * channels;
         ++img.Status();
      }

      // Finish JPEG compression.
      ::jpeg_finish_compress( jpeg_compressor );

      // Close the output file.
      ::fclose( fileData->handle ), fileData->handle = nullptr;

      // Release the JPEG compression object.
      ::jpeg_destroy_compress( jpeg_compressor );
      delete jpeg_compressor, fileData->compressor = nullptr;
   }
   catch ( ... )
   {
      if ( fileData->handle != nullptr )
         ::fclose( fileData->handle ), fileData->handle = nullptr;
      throw;
   }
}