void OpenJPEGImage::process( unsigned int res, int layers, int xoffset, int yoffset, unsigned int tw, unsigned int th, void *d )
{
  // Unfortunately, it's not currently possible to re-use OpenJPEG's stream or image structures,
  // so re-open if necessary
  if( !_image ) openImage();

  // Scale up our output bit depth to the nearest factor of 8
  unsigned int obpc = bpc;
  if( bpc <= 16 && bpc > 8 ) obpc = 16;
  else if( bpc <= 8 ) obpc = 8;

  unsigned int factor = 1;                  // Downsampling factor - set it to default value
  int vipsres = (numResolutions - 1) - res; // Reverse resolution number

  // Calculate number of extra resolutions needed that have not been encoded in the image
  if( res < virtual_levels ){
    factor = 2 * (virtual_levels - res);
    xoffset *= factor;
    yoffset *= factor;
    tw *= factor;
    th *= factor;
    // Set our resolution level back to the smallest original resolution
    vipsres = numResolutions - 1 - virtual_levels;
#ifdef DEBUG
  logfile << "OpenJPEG :: using smallest existing resolution " << virtual_levels << endl;
#endif
  }

  // Set the number of layers to half of the number of detected layers if we have not set the
  // layers parameter manually. If layers is set to less than 0, use all layers.
  if( layers < 0 ) layers = quality_layers;
  else if( layers == 0 ) layers = ceil( quality_layers/2.0 );

  // Also make sure we have at least 1 layer
  if( layers < 1 ) layers = 1;


  // Set number of quality layer and resolution
  opj_dparameters_t params;
  params.cp_layer = layers;
  params.cp_reduce = vipsres;


  if( !opj_setup_decoder( _codec, &params ) ){
    throw file_error( "OpenJPEG :: process() :: opj_setup_decoder() failed" );
  }


  // Set resolution - hack for openjpeg up to 2.2.0
  for( OPJ_UINT32 i = 0; i < _image->numcomps; i++ ){
    _image->comps[i].factor = vipsres;
  }

  // Image location and size at requested resolution
  unsigned int x0 = xoffset << vipsres;
  unsigned int y0 = yoffset << vipsres;
  unsigned int w0 = (xoffset + tw) << vipsres;
  unsigned int h0 = (yoffset + th) << vipsres;

#ifdef DEBUG
  logfile << "OpenJPEG :: decoding " << layers << " quality layers" << endl;
  logfile << "OpenJPEG :: requested region on high resolution canvas: position: "
	  << xoffset << "x" << yoffset << ". size: " << tw << "x" << th << endl;
  logfile << "OpenJPEG :: mapped resolution region size: " << (tw<<vipsres) << "x" << (th<<vipsres) << endl;
#endif


  // Define our decoding region
  if( !opj_set_decode_area( _codec, _image, x0, y0, w0, h0 ) ){
    throw file_error( "OpenJPEG :: process() :: opj_set_decode_area() failed" );
  }

  // Perform decoding
  if( !opj_decode( _codec, _stream, _image ) ){
    throw file_error( "OpenJPEG :: process() :: opj_decode() failed" );
  }


  // Extract any ICC profile - unfortunately, can only get ICC profile after decoding
  int icc_length = _image->icc_profile_len;
  const char* icc = (const char*) _image->icc_profile_buf;
  if( icc_length > 0 ) metadata["icc"] = string( icc, icc_length );
#ifdef DEBUG
  if( icc_length > 0 ){
    logfile << "OpenJPEG :: ICC profile detected with size " << icc_length << endl;
  }
#endif


  // Copy our decoded data by looping over all pixels
  unsigned int n = 0;
  unsigned int nk = 0;

  for( unsigned int j=0; j < th; j += factor ){
    for( unsigned int i = 0; i < tw; i += factor ){
      for( unsigned int k = 0; k < channels; k++ ){
        // Handle 16 and 8 bit data:
	// OpenJPEG's output data is 32 bit unsigned int, so just mask of the bottom 2 bytes
	// for 16 bit output or bottom 1 byte for 8 bit
	if( obpc == 16 ){
	  ((unsigned short*)d)[nk++] =(  (_image->comps[k].data[n]) & 0x0000ffff );
	}
	// Binary (bi-level) images need to be scaled up to 8 bits
	else if( bpc == 1 ){
	  ((unsigned char*)d)[nk++] = ((_image->comps[k].data[n]) & 0x000000f) * 255;
	}
	else{
	  ((unsigned char*)d)[nk++] = (_image->comps[k].data[n]) & 0x000000ff;
	}
      }
      n++;
    }
  }

  // We need to close the image here in case we try to use the OpenJPEG
  // stream or image structures multiple times in the same request pipeline
  closeImage();

}