Jump to content

Image::copy access violation exception


photo

Recommended Posts

Posted

Hi,
I'm trying to copy one channel (0) from R8 image to RGBA16 shading image (which have 4 channels), but I'm getting access violation during this process somewhere inside engine implementation.
In very short snippet this is what I'm doing:
 

Unigine::ImagePtr shadingImage = Unigine::Image::create( );
material.m_primaryMaterial->getTextureImage( material.m_primaryMaterial->findTexture( "shading" ), shadingImage );

Unigine::ImagePtr mapImage = Unigine::Image::create( texturePath.c_str( ) ); //texturePath is path to PNG/JPG R8 texture

shadingImage->copy( mapImage, 1, 0 ); //<- access violation here

It will be hard for me to reproduce it in some small sample, so I'm just looking for some hints what can cause this issue - what should I check before I try to use this method?

Posted

Hello arzezniczak,

It's hard to tell why you get access violation in that case. Normal behaviour would be error in the log as coping with different size per channel (8 bit to 16 bit) is not supported. You could try to copy information manually by iterating all pixels and setting values with get2D/set2D.

Posted

Yeah, I was expecting some error in log too.

Quote

coping with different size per channel (8 bit to 16 bit) is not supported

I must add one thing - before I use copy method on shading image I do some resize and convert to match size and "bittnes" (?) between this images:

const int mapWidth = mapImage->getWidth( );
const int mapHeight = mapImage->getHeight( );
if ( mapWidth != shadingImage->getWidth( ) || mapHeight != shadingImage->getHeight( ) )
	shadingImage->resize( mapWidth, mapHeight );

const int shadingFormat = shadingImage->getFormat( );
if ( mapImage->isUCharFormat( ) && (shadingFormat != Unigine::Image::FORMAT_RGBA8) ) //R8, RG8, RGB8, RGBA8, DXT1, DXT3, DXT5, ATI1 or ATI2
	shadingImage->convertToFormat( Unigine::Image::FORMAT_RGBA8 );
else if ( mapImage->isUShortFormat( ) && (shadingFormat != Unigine::Image::FORMAT_RGBA16) ) //R16, RG16, RGB16 or RGBA16
	shadingImage->convertToFormat( Unigine::Image::FORMAT_RGBA16 );
else if ( mapImage->isHalfFormat( ) && (shadingFormat != Unigine::Image::FORMAT_RGBA16F) ) //R16F, RG16F, RGB16F or RGBA16F
	shadingImage->convertToFormat( Unigine::Image::FORMAT_RGBA16F );
else if ( mapImage->isFloatFormat( ) && (shadingFormat != Unigine::Image::FORMAT_RGBA32F) ) //R32F, RG32F, RGB32F or RGBA32F
	shadingImage->convertToFormat( Unigine::Image::FORMAT_RGBA32F );

May this be a problem?

Posted

The code seems fine. If lifetimes of both images is ok I don't have any good guesses. You could still try with get2D/set2D and see if anything changes. It will show if the problem is inside "copy" method.

Posted (edited)

Well, I made manual copy using get2D and set2D and it's working just fine. It's a bit tailored to my needs (always copy only R component) but here is the code:

SetShadingImageFormat( mapImage, shadingImage ); //body of this function is in above post
//shadingImage->copy( mapImage, channel, 0 ); //copy commented out

//my manual copy
for ( int x = 0; x < mapImage->getWidth( ); ++x )
  for ( int y = 0; y < mapImage->getHeight( ); ++y )
  	SetPixelRedChannelIn( x, y, channel, mapImage->get2D( x, y ), shadingImage );
  
...
  
void SetPixelRedChannelIn( int x, int y, int channel, Unigine::Image::Pixel pixel, const Unigine::ImagePtr &image )
{
	Unigine::Image::Pixel p = image->get2D( x, y ); //orginal pixel value - we will get two components from it
  
  	//we are not using microfiber - that's why last component is always maxed out

  	if ( image->isUCharFormat( ) || image->isUShortFormat( ) )
  	{
  		if ( channel == 0 )
  			image->set2D( x, y, Unigine::Image::Pixel( pixel.i.r, p.i.g, p.i.b, image->isUCharFormat( ) ? 255 : 65535 ) );
  		else if ( channel == 1 )
  			image->set2D( x, y, Unigine::Image::Pixel( p.i.r, pixel.i.r, p.i.b, image->isUCharFormat( ) ? 255 : 65535 ) );
  		else if ( channel == 2 )
  			image->set2D( x, y, Unigine::Image::Pixel( p.i.r, p.i.g, pixel.i.r, image->isUCharFormat( ) ? 255 : 65535 ) );
 	}
	else if ( image->isHalfFormat( ) || image->isFloatFormat( ) )
 	{
  		if ( channel == 0 )
  			image->set2D( x, y, Unigine::Image::Pixel( pixel.f.r, p.f.g, p.f.b, 1.0 ) );
  		else if ( channel == 1 )
  			image->set2D( x, y, Unigine::Image::Pixel( p.f.r, pixel.f.r, p.f.b, 1.0 ) );
  		else if ( channel == 2 )
  			image->set2D( x, y, Unigine::Image::Pixel( p.f.r, p.f.g, pixel.f.r, 1.0 ) );
  	}
  	else
  		Unigine::Log::fatal( "[SetPixelRedChannelIn] image format nor supported: %s", image->getFormatName( ) );
}

 

I have one more question a little bit realted to this topic: why Image::set( int channel, int value ) have only int as value and there is no overload for float? What if I have half or float format image and I want to set whole channel to one value? Do I must set2D manually for all pixels?

Edited by arzezniczak
Posted

set2D/get2D is the most simple and straight way for any manipulation with images. If performance overhead isn't an issue I prefer it.

I believe Image::set can be used for halfs and floats as well in a low level way. Int value will be reinterpret as a float value with the same bit pattern. You can use union { float; int} for setting float value and struct half (https://developer.unigine.com/en/docs/2.8/api/library/math/class.half?rlang=cpp ) for half value.

Also you can get pointer to data with Image::getPixels and implement your own copy methods

Posted

Also you may find handy using toVec4/toPixel functions to take care of formats. SetPixelRedChannelIn in that case may look like this:

void SetRedPixelChannelIn(int x, int y, int channel, const ImagePtr &src, const ImagePtr &dst)
{
	const vec4 src_pixel = src->toVec4(src->get2D(x, y));
	vec4 dst_pixel = dst->toVec4(dst->get2D(x, y));
	dst_pixel[channel] = src_pixel.x;
	dst->set2D(x, y, dst->toPixel(dst_pixel));
}

 

×
×
  • Create New...