예제 #1
0
int main (int argc, char *argv[])
{
	opj_dparameters_t l_param;
	opj_codec_t * l_codec;
	opj_image_t * l_image;
	FILE * l_file;
	opj_stream_t * l_stream;
	OPJ_UINT32 l_data_size;
	OPJ_UINT32 l_max_data_size = 1000;
	OPJ_UINT32 l_tile_index;
	OPJ_BYTE * l_data = (OPJ_BYTE *) malloc(1000);
	opj_bool l_go_on = OPJ_TRUE;
	OPJ_INT32 l_tile_x0,l_tile_y0;
	OPJ_UINT32 l_tile_width,l_tile_height,l_nb_tiles_x,l_nb_tiles_y,l_nb_comps;
	OPJ_INT32 l_current_tile_x0,l_current_tile_y0,l_current_tile_x1,l_current_tile_y1;

  int da_x0=0;
  int da_y0=0;
  int da_x1=1000;
  int da_y1=1000;
  char input_file[64];
	
  /* should be test_tile_decoder 0 0 1000 1000 tte1.j2k */
  if( argc == 6 )
    {
    da_x0=atoi(argv[1]);
    da_y0=atoi(argv[2]);
    da_x1=atoi(argv[3]);
    da_y1=atoi(argv[4]);
    strcpy(input_file,argv[5]);
    }
  else
    {
    da_x0=0;
    da_y0=0;
    da_x1=1000;
    da_y1=1000;
    strcpy(input_file,"test.j2k");
    }

	if
		(! l_data)
	{
		return 1;
	}
	opj_set_default_decoder_parameters(&l_param);

	/** you may here add custom decoding parameters */
	/* do not use layer decoding limitations */
	l_param.cp_layer = 0;

	/* do not use resolutions reductions */
	l_param.cp_reduce = 0;

	/* to decode only a part of the image data */
	//opj_restrict_decoding(&l_param,0,0,1000,1000);
	
	l_codec = opj_create_decompress_v2(CODEC_J2K);
	if
		(! l_codec)
	{
		free(l_data);
		return 1;
	}

	/* catch events using our callbacks and give a local context */		
	opj_set_info_handler(l_codec, info_callback,00);
	opj_set_warning_handler(l_codec, warning_callback,00);
	opj_set_error_handler(l_codec, error_callback,00);
	
	if
		(! opj_setup_decoder_v2(l_codec,&l_param))
	{
		free(l_data);
		opj_destroy_codec(l_codec);
		return 1;
	}
	
	l_file = fopen(input_file,"rb");
	if
		(! l_file)
	{
		fprintf(stdout, "Error opening input file\n");
		free(l_data);
		opj_destroy_codec(l_codec);
		return 1;
	}

	l_stream = opj_stream_create_default_file_stream(l_file,OPJ_TRUE);

	if
		(! opj_read_header(l_stream, l_codec, &l_image))
	{
		free(l_data);
		opj_stream_destroy(l_stream);
		fclose(l_file);
		opj_destroy_codec(l_codec);
		return 1;
	}
	printf("Setting decoding area to %d,%d,%d,%d\n", da_x0, da_y0, da_x1, da_y1);
	opj_set_decode_area(l_codec, l_image, da_x0, da_y0, da_x1, da_y1);
	while
		(l_go_on)
	{
		if
			(! opj_read_tile_header(
						l_codec,
            l_stream,
						&l_tile_index,
						&l_data_size,
						&l_current_tile_x0,
						&l_current_tile_y0,
						&l_current_tile_x1,
						&l_current_tile_y1,
						&l_nb_comps,
						&l_go_on))
		{
			free(l_data);
			opj_stream_destroy(l_stream);
			fclose(l_file);
			opj_destroy_codec(l_codec);
			opj_image_destroy(l_image);
			return 1;
		}
		if
			(l_go_on)
		{
			if
				(l_data_size > l_max_data_size)
			{
				l_data = (OPJ_BYTE *) realloc(l_data,l_data_size);
				if
					(! l_data)
				{
					opj_stream_destroy(l_stream);
					fclose(l_file);
					opj_destroy_codec(l_codec);
					opj_image_destroy(l_image);
					return 1;
				}
				l_max_data_size = l_data_size;
			}

			if
				(! opj_decode_tile_data(l_codec,l_tile_index,l_data,l_data_size,l_stream))
			{
				free(l_data);
				opj_stream_destroy(l_stream);
				fclose(l_file);
				opj_destroy_codec(l_codec);
				opj_image_destroy(l_image);
				return 1;
			}
			/** now should inspect image to know the reduction factor and then how to behave with data */
		}
	}
	if
		(! opj_end_decompress(l_codec,l_stream))
	{
		free(l_data);
		opj_stream_destroy(l_stream);
		fclose(l_file);
		opj_destroy_codec(l_codec);
		opj_image_destroy(l_image);
		return 1;
	}
	free(l_data);
	opj_stream_destroy(l_stream);
	fclose(l_file);
	opj_destroy_codec(l_codec);
	opj_image_destroy(l_image);

	// Print profiling
	//PROFPRINT();

	return 0;
}
예제 #2
0
bool ossim::opj_decode( std::ifstream* in,
                        const ossimIrect& rect,
                        ossim_uint32 resLevel,
                        ossim_int32 format, // OPJ_CODEC_FORMAT
                        std::streamoff fileOffset,
                        ossimImageData* tile)
{
   static const char MODULE[] = "ossimOpjDecoder::decode";

   bool status = false;

   if ( traceDebug() )
   {
      ossimNotify(ossimNotifyLevel_DEBUG)
         << MODULE << "entered...\nrect: " << rect
         << "\nresLevel: " << resLevel << std::endl;
   }
   
   // Need to check for NAN in rect
   if ( in && tile && !rect.hasNans())
   {
      in->seekg( fileOffset, std::ios_base::beg );

      opj_dparameters_t param;
      opj_codec_t*      codec = 0;
      opj_image_t*      image = 0;;
      opj_stream_t*     stream = 0;

      opj_user_istream* userStream = new opj_user_istream();
      userStream->m_str = in;
      userStream->m_offset = fileOffset;

      /* Set the length to avoid an assert */
      in->seekg(0, std::ios_base::end);

      // Fix: length must be passed in for nift blocks.
      userStream->m_length = in->tellg();

      // Set back to front:
      in->clear();
      in->seekg(fileOffset, std::ios_base::beg);
      
      stream = opj_stream_default_create(OPJ_TRUE);
      if (!stream)
      {
         opj_stream_destroy(stream);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_setup_decoder failed!";
         throw ossimException(errMsg);
      }
      
      opj_stream_set_read_function(stream, ossim_opj_istream_read);
      opj_stream_set_skip_function(stream, ossim_opj_istream_skip);
      opj_stream_set_seek_function(stream, ossim_opj_istream_seek);

      // Fix: length must be passed in for nift blocks.
      opj_stream_set_user_data_length(stream, userStream->m_length);
      
      opj_stream_set_user_data(stream, userStream,
                               ossim_opj_free_user_istream_data);

      opj_stream_set_user_data_length(stream, userStream->m_length);

      
      /* Set the default decoding parameters */
      opj_set_default_decoder_parameters(&param);

      param.decod_format = format;

      /** you may here add custom decoding parameters */
      /* do not use layer decoding limitations */
      param.cp_layer = 0;

      /* do not use resolutions reductions */
      param.cp_reduce = resLevel;

      codec = opj_create_decompress( (CODEC_FORMAT)format );

      // catch events using our callbacks and give a local context
      //opj_set_info_handler   (codec, ossim::opj_info_callback,   00);
      opj_set_info_handler   (codec, NULL,   00);
      opj_set_warning_handler(codec, ossim::opj_warning_callback,00);
      opj_set_error_handler  (codec, ossim::opj_error_callback,  00);

      // Setup the decoder decoding parameters using user parameters
      if ( opj_setup_decoder(codec, &param) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_setup_decoder failed!";
         throw ossimException(errMsg);
      }

      // Read the main header of the codestream and if necessary the JP2 boxes.
      if ( opj_read_header(stream, codec, &image) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);         
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_read_header failed!";
         throw ossimException(errMsg);
      }

      // tmp drb:
      // opj_stream_destroy(stream);
      // return;
      
      if ( opj_set_decoded_resolution_factor(codec, resLevel) == false)
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);
         std::string errMsg = MODULE;
         errMsg += " ERROR:  opj_set_decoded_resolution_factor failed!";
         throw ossimException(errMsg);
      }

      //ossim_float32 res = resLevel;
      ossimIrect resRect = rect * (1 << resLevel);

      //std::cout << "resRect.ul(): " << resRect.ul()
      //          << "\nresRect.lr(): " << resRect.lr()
      //          << std::endl;

//      if ( opj_set_decode_area(codec, image, rect.ul().x, rect.ul().y,
//                               rect.lr().x+1, rect.lr().y+1) == false )
      if ( opj_set_decode_area(codec, image, resRect.ul().x, resRect.ul().y,
                               resRect.lr().x+1, resRect.lr().y+1) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_set_decode_area failed!";
         throw ossimException(errMsg);
      }

      // Get the decoded image:
      if ( opj_decode(codec, stream, image) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_decode failed!";
         throw ossimException(errMsg);
      }
      
      // ossim::print(std::cout, *image);
      if(image->icc_profile_buf) {
#if defined(OPJ_HAVE_LIBLCMS1) || defined(OPJ_HAVE_LIBLCMS2)
          color_apply_icc_profile(image); /* FIXME */
#endif
          free(image->icc_profile_buf);
          image->icc_profile_buf = NULL; image->icc_profile_len = 0;
      }

      status = ossim::copyOpjImage(image, tile);
      
#if 0
      ossim_uint32 tile_index      = 0;
      ossim_uint32 data_size       = 0;
      ossim_int32  current_tile_x0 = 0;
      ossim_int32  current_tile_y0 = 0;
      ossim_int32  current_tile_x1 = 0;
      ossim_int32  current_tile_y1 = 0;
      ossim_uint32 nb_comps        = 0;
      OPJ_BOOL go_on = 1;

      if ( opj_read_tile_header( codec,
                                 stream,
                                 &tile_index,
                                 &data_size,
                                 &current_tile_x0,
                                 &current_tile_y0,
                                 &current_tile_x1,
                                 &current_tile_y1,
                                 &nb_comps,
                                 &go_on) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_read_tile_header failed!";
         throw ossimException(errMsg);
      }
#endif

#if 0
      std::cout << "tile_index:      " << tile_index
                << "\ndata_size:       " << data_size
                << "\ncurrent_tile_x0: " << current_tile_x0
                << "\ncurrent_tile_y0: " << current_tile_y0
                << "\ncurrent_tile_x1: " << current_tile_x1
                << "\ncurrent_tile_y1: " << current_tile_y1
                << "\nnb_comps:        " << nb_comps
                << std::endl;
#endif

#if 0
      if ( opj_decode_tile_data(codec, tile_index,l_data,l_data_size,l_stream) == false)
      {
         opj_stream_destroy(l_stream);
         opj_destroy_codec(l_codec);
         opj_image_destroy(l_image);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_read_tile_header failed!";
         throw ossimException(errMsg);
      }
#endif

#if 0
      
      if (opj_end_decompress(codec,stream) == false )
      {
         opj_stream_destroy(stream);
         opj_destroy_codec(codec);
         opj_image_destroy(image);
         std::string errMsg = MODULE;
         errMsg += " ERROR: opj_end_decompress failed!";
         throw ossimException(errMsg);
      } 

#endif
      
      /* Free memory */
      opj_stream_destroy(stream);
      opj_destroy_codec(codec);
      opj_image_destroy(image);

      // Tmp drb:
      if ( in->eof() )
      {
         in->clear();
      }
      in->seekg(fileOffset, std::ios_base::beg );
      
   } // Matches: if ( in && tile )

   return status;
   
} // End: ossim::opj_decode( ... )
예제 #3
0
OpenJPEGReader_readRegion(J2K_USER_DATA *data, nrt_Uint32 x0, nrt_Uint32 y0,
                          nrt_Uint32 x1, nrt_Uint32 y1, nrt_Uint8 **buf,
                          nrt_Error *error)
{
    OpenJPEGReaderImpl *impl = (OpenJPEGReaderImpl*) data;

    opj_stream_t *stream = NULL;
    opj_image_t *image = NULL;
    opj_codec_t *codec = NULL;
    nrt_Uint64 bufSize;
    nrt_Uint64 offset = 0;
    nrt_Uint32 componentBytes, nComponents;

    if (!OpenJPEG_setup(impl, &stream, &codec, error))
    {
        goto CATCH_ERROR;
    }

    /* unfortunately, we need to read the header every time ... */
    if (!opj_read_header(stream, codec, &image))
    {
        /*nrt_Error_init(error, "Error reading header", NRT_CTXT, NRT_ERR_UNK);*/
        goto CATCH_ERROR;
    }

    if (x1 == 0)
        x1 = j2k_Container_getWidth(impl->container, error);
    if (y1 == 0)
        y1 = j2k_Container_getHeight(impl->container, error);

    /* only decode what we want */
    if (!opj_set_decode_area(codec, image, x0, y0, x1, y1))
    {
        /*nrt_Error_init(error, "Error decoding area", NRT_CTXT, NRT_ERR_UNK);*/
        goto CATCH_ERROR;
    }

    nComponents = j2k_Container_getNumComponents(impl->container, error);
    componentBytes = (j2k_Container_getPrecision(impl->container, error) - 1) / 8 + 1;
    bufSize = (nrt_Uint64)(x1 - x0) * (y1 - y0) * componentBytes * nComponents;
    if (buf && !*buf)
    {
        *buf = (nrt_Uint8*)J2K_MALLOC(bufSize);
        if (!*buf)
        {
            nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT,
                           NRT_ERR_MEMORY);
            goto CATCH_ERROR;
        }
    }

    {
        int keepGoing;
        OPJ_UINT32 tileIndex, reqSize;
        OPJ_INT32 tileX0, tileY0, tileX1, tileY1;

        do
        {
            if (!opj_read_tile_header(codec, stream, &tileIndex, &reqSize, &tileX0,
                                      &tileY0, &tileX1, &tileY1, &nComponents,
                                      &keepGoing))
            {
                /*nrt_Error_init(error, "Error reading tile header", NRT_CTXT,
                  NRT_ERR_UNK);*/
                goto CATCH_ERROR;
            }

            if (keepGoing)
            {
                if (!opj_decode_tile_data(codec, tileIndex, (*buf + offset),
                                          reqSize, stream))
                {
                    /*nrt_Error_init(error, "Error decoding tile", NRT_CTXT,
                      NRT_ERR_UNK);*/
                    goto CATCH_ERROR;
                }
                offset += reqSize;
            }
        }
        while (keepGoing);
    }

    goto CLEANUP;

    CATCH_ERROR:
    {
        bufSize = 0;
    }

    CLEANUP:
    {
        OpenJPEG_cleanup(&stream, &codec, &image);
    }
    return bufSize;
}
예제 #4
0
int main (int argc, char *argv[])
{
        opj_dparameters_t l_param;
        opj_codec_t * l_codec;
        opj_image_t * l_image;
        opj_stream_t * l_stream;
        OPJ_UINT32 l_data_size;
        OPJ_UINT32 l_max_data_size = 1000;
        OPJ_UINT32 l_tile_index;
        OPJ_BYTE * l_data = (OPJ_BYTE *) malloc(1000);
        OPJ_BOOL l_go_on = OPJ_TRUE;
        OPJ_UINT32 l_nb_comps=0 ;
        OPJ_INT32 l_current_tile_x0,l_current_tile_y0,l_current_tile_x1,l_current_tile_y1;

        int da_x0=0;
        int da_y0=0;
        int da_x1=1000;
        int da_y1=1000;
        char input_file[64];

        /* should be test_tile_decoder 0 0 1000 1000 tte1.j2k */
        if( argc == 6 )
        {
                da_x0=atoi(argv[1]);
                da_y0=atoi(argv[2]);
                da_x1=atoi(argv[3]);
                da_y1=atoi(argv[4]);
                strcpy(input_file,argv[5]);

        }
        else
        {
                da_x0=0;
                da_y0=0;
                da_x1=1000;
                da_y1=1000;
                strcpy(input_file,"test.j2k");
        }

        if (! l_data) {
                return EXIT_FAILURE;
        }

        l_stream = opj_stream_create_default_file_stream(input_file,OPJ_TRUE);
        if (!l_stream){
                free(l_data);
                fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
                return EXIT_FAILURE;
        }

        /* Set the default decoding parameters */
        opj_set_default_decoder_parameters(&l_param);

        /* */
        l_param.decod_format = infile_format(input_file);

        /** you may here add custom decoding parameters */
        /* do not use layer decoding limitations */
        l_param.cp_layer = 0;

        /* do not use resolutions reductions */
        l_param.cp_reduce = 0;

        /* to decode only a part of the image data */
        /*opj_restrict_decoding(&l_param,0,0,1000,1000);*/


        switch(l_param.decod_format) {
                case J2K_CFMT:	/* JPEG-2000 codestream */
                        {
                                /* Get a decoder handle */
                                l_codec = opj_create_decompress(OPJ_CODEC_J2K);
                                break;
                        }
                case JP2_CFMT:	/* JPEG 2000 compressed image data */
                        {
                                /* Get a decoder handle */
                                l_codec = opj_create_decompress(OPJ_CODEC_JP2);
                                break;
                        }
                default:
                        {
                                fprintf(stderr, "ERROR -> Not a valid JPEG2000 file!\n");
                                free(l_data);
                                opj_stream_destroy(l_stream);
                                return EXIT_FAILURE;
                        }
        }

        /* catch events using our callbacks and give a local context */
        opj_set_info_handler(l_codec, info_callback,00);
        opj_set_warning_handler(l_codec, warning_callback,00);
        opj_set_error_handler(l_codec, error_callback,00);

        /* Setup the decoder decoding parameters using user parameters */
        if (! opj_setup_decoder(l_codec, &l_param))
        {
                fprintf(stderr, "ERROR -> j2k_dump: failed to setup the decoder\n");
                free(l_data);
                opj_stream_destroy(l_stream);
                opj_destroy_codec(l_codec);
                return EXIT_FAILURE;
        }

        /* Read the main header of the codestream and if necessary the JP2 boxes*/
        if (! opj_read_header(l_stream, l_codec, &l_image))
        {
                fprintf(stderr, "ERROR -> j2k_to_image: failed to read the header\n");
                free(l_data);
                opj_stream_destroy(l_stream);
                opj_destroy_codec(l_codec);
                return EXIT_FAILURE;
        }

        if (!opj_set_decode_area(l_codec, l_image, da_x0, da_y0,da_x1, da_y1)){
                fprintf(stderr,	"ERROR -> j2k_to_image: failed to set the decoded area\n");
                free(l_data);
                opj_stream_destroy(l_stream);
                opj_destroy_codec(l_codec);
                opj_image_destroy(l_image);
                return EXIT_FAILURE;
        }


        while (l_go_on)
        {
                if (! opj_read_tile_header( l_codec,
                                        l_stream,
                                        &l_tile_index,
                                        &l_data_size,
                                        &l_current_tile_x0,
                                        &l_current_tile_y0,
                                        &l_current_tile_x1,
                                        &l_current_tile_y1,
                                        &l_nb_comps,
                                        &l_go_on))
                {
                        free(l_data);
                        opj_stream_destroy(l_stream);
                        opj_destroy_codec(l_codec);
                        opj_image_destroy(l_image);
                        return EXIT_FAILURE;
                }

                if (l_go_on)
                {
                        if (l_data_size > l_max_data_size)
                        {
                                OPJ_BYTE *l_new_data = (OPJ_BYTE *) realloc(l_data, l_data_size);
                                if (! l_new_data)
                                {
                                        free(l_new_data);
                                        opj_stream_destroy(l_stream);
                                        opj_destroy_codec(l_codec);
                                        opj_image_destroy(l_image);
                                        return EXIT_FAILURE;
                                }
                                l_data = l_new_data;
                                l_max_data_size = l_data_size;
                        }

                        if (! opj_decode_tile_data(l_codec,l_tile_index,l_data,l_data_size,l_stream))
                        {
                                free(l_data);
                                opj_stream_destroy(l_stream);
                                opj_destroy_codec(l_codec);
                                opj_image_destroy(l_image);
                                return EXIT_FAILURE;
                        }
                        /** now should inspect image to know the reduction factor and then how to behave with data */
                }
        }

        if (! opj_end_decompress(l_codec,l_stream))
        {
                free(l_data);
                opj_stream_destroy(l_stream);
                opj_destroy_codec(l_codec);
                opj_image_destroy(l_image);
                return EXIT_FAILURE;
        }

        /* Free memory */
        free(l_data);
        opj_stream_destroy(l_stream);
        opj_destroy_codec(l_codec);
        opj_image_destroy(l_image);

        /* Print profiling*/
        /*PROFPRINT();*/

        return EXIT_SUCCESS;
}
예제 #5
0
OpenJPEGReader_readTile(J2K_USER_DATA *data, nrt_Uint32 tileX, nrt_Uint32 tileY,
                  nrt_Uint8 **buf, nrt_Error *error)
{
    OpenJPEGReaderImpl *impl = (OpenJPEGReaderImpl*) data;

    opj_stream_t *stream = NULL;
    opj_image_t *image = NULL;
    opj_codec_t *codec = NULL;
    nrt_Uint32 bufSize;
    const OPJ_UINT32 tileWidth = j2k_Container_getTileWidth(impl->container, error);
    const OPJ_UINT32 tileHeight = j2k_Container_getTileHeight(impl->container, error);
    size_t numBitsPerPixel = 0;
    size_t numBytesPerPixel = 0;
    nrt_Uint64 fullBufSize = 0;

    if (!OpenJPEG_setup(impl, &stream, &codec, error))
    {
        goto CATCH_ERROR;
    }

    /* unfortunately, we need to read the header every time ... */
    if (!opj_read_header(stream, codec, &image))
    {
        /*nrt_Error_init(error, "Error reading header", NRT_CTXT, NRT_ERR_UNK);*/
        goto CATCH_ERROR;
    }

    /* only decode what we want */
    if (!opj_set_decode_area(codec, image, tileWidth * tileX, tileHeight * tileY,
                             tileWidth * (tileX + 1), tileHeight * (tileY + 1)))
    {
        /*nrt_Error_init(error, "Error decoding area", NRT_CTXT, NRT_ERR_UNK);*/
        goto CATCH_ERROR;
    }

    {
        int keepGoing;
        OPJ_UINT32 tileIndex, nComponents;
        OPJ_INT32 tileX0, tileY0, tileX1, tileY1;

        if (!opj_read_tile_header(codec, stream, &tileIndex, &bufSize, &tileX0,
                                  &tileY0, &tileX1, &tileY1, &nComponents,
                                  &keepGoing))
        {
            /*nrt_Error_init(error, "Error reading tile header", NRT_CTXT,
              NRT_ERR_UNK);*/
            goto CATCH_ERROR;
        }

        if (keepGoing)
        {
            /* TODO: The way blockIO->cntl->blockOffsetInc is currently
             *       implemented in ImageIO.c corresponds with how a
             *       non-compressed partial block would be laid out in a
             *       NITF - the actual extra columns would have been read.
             *       Not sure how the J2K data is laid out on disk but
             *       OpenJPEG is hiding this from us if the extra columns are
             *       present there.  So whenever we get a partial tile that
             *       isn't at the full width, we need to add in these extra
             *       columns of 0's ourselves.  Potentially we could update
             *       ImageIO.c to not require this instead.  Note that we
             *       don't need to pad out the extra rows for a partial block
             *       that isn't the full height because ImageIO will never try
             *       to memcpy these in - we only need to get the stride to
             *       work out correctly.
             */
            const OPJ_UINT32 thisTileWidth = tileX1 - tileX0;
            const OPJ_UINT32 thisTileHeight = tileY1 - tileY0;
            if (thisTileWidth < tileWidth)
            {
                /* TODO: The current approach below only works for single band
                 *       imagery.  For RGB data, I believe it is stored as all
                 *       red, then all green, then all blue, so we would need
                 *       a temp buffer rather than reusing the current buffer.
                 */
                if (nComponents != 1)
                {
                    nrt_Error_init(
                        error,
                        "Partial tile width not implemented for multi-band",
                        NRT_CTXT, NRT_ERR_UNK);
                    goto CATCH_ERROR;
                }

                numBitsPerPixel =
                    j2k_Container_getPrecision(impl->container, error);
                numBytesPerPixel =
                    (numBitsPerPixel / 8) + (numBitsPerPixel % 8 != 0);
                fullBufSize = tileWidth * thisTileHeight * numBytesPerPixel;
            }
            else
            {
                fullBufSize = bufSize;
            }

            if (buf && !*buf)
            {
                *buf = (nrt_Uint8*)J2K_MALLOC(fullBufSize);
                if (!*buf)
                {
                    nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT,
                                   NRT_ERR_MEMORY);
                    goto CATCH_ERROR;
                }
            }

            if (!opj_decode_tile_data(codec, tileIndex, *buf, bufSize, stream))
            {
                /*nrt_Error_init(error, "Error decoding tile", NRT_CTXT,
                  NRT_ERR_UNK);*/
                goto CATCH_ERROR;
            }

            if (thisTileWidth < tileWidth)
            {
                /* We have a tile that isn't as wide as it "should" be
                 * Need to add in the extra columns ourselves.  By marching
                 * through the rows backwards, we can do this in place.
                 */
                const size_t srcStride = thisTileWidth * numBytesPerPixel;
                const size_t destStride = tileWidth * numBytesPerPixel;
                const size_t numLeftoverBytes = destStride - srcStride;
                OPJ_UINT32 lastRow = thisTileHeight - 1;
                size_t srcOffset = lastRow * srcStride;
                size_t destOffset = lastRow * destStride;
                OPJ_UINT32 ii;
                nrt_Uint8* bufPtr = *buf;

                for (ii = 0;
                     ii < thisTileHeight;
                     ++ii, srcOffset -= srcStride, destOffset -= destStride)
                {
                    nrt_Uint8* const dest = bufPtr + destOffset;
                    memmove(dest, bufPtr + srcOffset, srcStride);
                    memset(dest + srcStride, 0, numLeftoverBytes);
                }
            }
        }
    }

    goto CLEANUP;

    CATCH_ERROR:
    {
        fullBufSize = 0;
    }

    CLEANUP:
    {
        OpenJPEG_cleanup(&stream, &codec, &image);
    }
    return fullBufSize;
}