arzezniczak Posted May 7, 2019 Posted May 7, 2019 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?
andrey-kozlov Posted May 7, 2019 Posted May 7, 2019 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.
arzezniczak Posted May 7, 2019 Author Posted May 7, 2019 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?
andrey-kozlov Posted May 7, 2019 Posted May 7, 2019 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.
arzezniczak Posted May 8, 2019 Author Posted May 8, 2019 (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 May 8, 2019 by arzezniczak
andrey-kozlov Posted May 8, 2019 Posted May 8, 2019 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
andrey-kozlov Posted May 11, 2019 Posted May 11, 2019 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)); }
Recommended Posts