Preserving Alpha values when using writeImage with a surface16u

Greetings,

I’m trying to manipulate the alpha values of a PNG image and preserve the manipulated values after writing the image.

I’m loading the image using the following:

mSurface16 = Surface16u::create( loadImage( path ) );

Then manipulating the alpha values like so:

for (int i = 0; i < 300 ; i ++)
{
    auto pixel = mSurface16->getPixel(ivec2(i,0));
    mSurface16->setPixel(ivec2(i,0), ColorA(pixel.r, pixel.g, pixel.b, pixel.a - i));
    cout << "pixels new alpha: " << pixel.a << endl;
}

when reloading the image using:

    mSurface16 = Surface16u::create( loadImage( path ) );
    
    for (int i = 0; i < 300; i++)
    {
       auto pixel = mSurface16->getPixel( ivec2(i,0) );
       cout << "pixelalpha: " << pixel.a << endl;
    }

The values all remain unchanged at 65535…

Any thoughts would be welcomed.

Thanks!

Are you saving the file? The setPixel function only manipulates data in RAM.

Hi Paul,

Yes i am, stupid mistake leaving that part out… Using:

fs::path path = getSaveFilePath();
if( ! path.empty() ) {
   // Surface16u s16( mTexture->createSource() );
    writeImage( writeFile( path ), *mSurface16);
}

Reloading the file after writing to disk yields all values of 65535 for alpha using mSurface16->getPixel(ivec(i,j))

Does the PNG have an alpha channel to begin with? If not, getPixel will always return the maximum value and setPixel will ignore the alpha value.

Also, I’m not sure setPixel supports 16-bit PNG’s, judging by the source code. I could be wrong, though.

Hi Paul,

As a point, the original image does not have an alpha channel. However, if I ignore the fact that I’m loading an image and simply create a surface and populate each pixel with random values, it seems to work. For example:

mSurfaceOut = Surface16u::create(1024,768,true); // changed from Surface::create
int w = mSurface16->getWidth();
int h = mSurface16->getHeight();
    
    for (int i = 0; i < w ; i ++)
    {
        for (int j = 0; j < h ; j ++)
        {
            int16_t r = randInt(0, 65535);
            int16_t g = randInt(0, 65535);
            int16_t b = randInt(0, 65535);
            int16_t a = randInt(0, 65535);
            
        mSurfaceOut->setPixel(ivec2(i,j), ColorA(r, g, b, a));
        //cout << "pixels new alpha: " << pixel.a << endl;
        }
    }

will produce an image like this :slight_smile:

Something to note here is that if I declare any of the r,g,b, or a variables as int32_t this does not work.

However, when I then load this image in using:
mSurface16 = Surface16u::create( loadImage( path ) );

The r,g,b, and a values are either 0 or 65535. The image I’ve included above is definitely a 16-bit image and contains an alpha channel… I’m just getting a bit lost as to why it’s maxing/zeroing out the values, and why it’s not preserving any changes to the alpha channel.

Shouldn’t the first line above be

mSurfaceOut = Surface16u::create(1024,768,true);

? And mSurfaceOut should be of type Surface16u, as Surface uses uin8_ts.

You’re quite right Rich, it is that way in my code, I wrote this reply away from my laptop and didn’t have the code on hand. The line in the code is in fact:

mSurfaceOut = Surface16u::create(1024, 768, true);

So yeah, make sure that the original PNG actually has an alpha channel, otherwise neither the getPixel or setPixel function will do anything meaningful. Also, use uint16_t (unsigned), otherwise you’ll get invalid results for values above 32767.

Thanks Paul, I’ll give this a try and let you know. Is a viable workaround for loading an image that has no alpha channel, like a JPG, creating a new Surface!6u with an alpha channel ( mSurfaceOut = Surface16u::create(1024, 768, true) ) and manually setting RGB values based on the original image’s color data, then giving it an alpha value of my choosing?

I guess so, but JPG’s are always 8-bit as far as I know, so you’d have to only set the hi byte of the 16-bit value and set the lo byte to zero (in other words: convert 8-bit to 16-bit). Be careful to properly skip/set the 16-bit alpha word, based on whether you’re using ARGB or RGBA. And of course be aware of endianness. You’ll probably have to experiment a little.

Thanks Paul, as always mich obliged.