Image Transparency

miker1264 · 262

miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
on: August 03, 2022, 06:33:42 AM
In my recent adventures experimenting with AROS graphics programs I discovered 3 types of 8bit image Transparency. There is Binary Transparency such as with GIF images & ILBM images that uses a single transparent color. There is also Simple Transparency such as 8bit PNG images with tRNS Data Chunk that contains 8bit alpha values that correspond to each of the colors in the PLTE Chunk. When using the tRNS Transparency values the colormap & indices need to be re-arranged into a Lower Range containing all the transparent values at the beginning & an Upper Range containing Opaque Values of 255. For tRNS values we only use the Lower Range. All other values after that are assumed to be fully opaque at 255. So what's the problem?

While testing my DT2PNG command line app that uses Datatypes & Neuquant Color Quantizing to convert 32bit images into 8bit PNG images I noticed something odd. Windows 10 displayed 8bit PNG images with tRNS Chunk correctly as Transparent PNG. Linux also had no problem displaying the Transparent PNG. But AROS PNG Datatype & hence Mutiview showed an outer area of Transparency with a black ring where the alpha mask would normally be. But why does PNG Datatype do that?

The AROS PNG Datatype by nature does not handle tRNS Values correctly. It merely finds the most suitable of the values for a single transparent color. It assigns mskhasmask & transparent color in the Bitmap Header & it discards the other values. Datatypes only need one transparent color. But that does nothing to help display 8bit PNG images with tRNS values.

I would like to have at least one AROS Graphics App that displays the tRNS values correctly so we can show 8bit PNG images & 8bit GIF images with Transparency. I'm thinking that Luna Paint might require some more experimenting in that area to display tRNS values. While I'm there I might add Neuquant Color Quantizing & more Datatype Save Options & a Change Log for updates including all the functions added a year ago.

Having fun with pixels!  :)
« Last Edit: August 03, 2022, 06:43:49 AM by miker1264 »



deadwood

  • AROS Developer
  • Legendary Member
  • *****
    • Posts: 1054
    • Karma: +104/-0
Reply #1 on: August 03, 2022, 07:09:54 AM
The AROS PNG Datatype by nature does not handle tRNS Values correctly. It merely finds the most suitable of the values for a single transparent color. It assigns mskhasmask & transparent color in the Bitmap Header & it discards the other values. Datatypes only need one transparent color. But that does nothing to help display 8bit PNG images with tRNS values.

Hmm, I was debugging this recently for Scalos where it has some 8-bit transparent images and I think how it is handled is that 8-bit image is converted to 32-bit image so that all transparency tRNS values "are preserved". See here:

https://github.com/deadw00d/AROS/blob/alt-abiv0/workbench/classes/datatypes/png/pngclass.c#L314



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #2 on: August 03, 2022, 07:46:16 AM
@deadwood

Interesting. This requires further investigation on my part.

The most recent PNG Datatype is 42.3 from 3 months ago. It has been recently updated.

I was looking at version 42.0 from a few years ago. There is a big difference in PNG Paletted Images Code.

I compiled version 42.3 for x86-64 & it displayed the 8bit PNG image with tRNS Data correctly this time.  ;)

See Samples:
« Last Edit: August 03, 2022, 08:26:26 AM by miker1264 »



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #3 on: August 03, 2022, 09:02:38 PM
Now that I'm sure that PNG Datatype can display the tRNS Data correctly I'm re-writing my command line app DT2PNG to accept an --alpha switch for producing a tRNS Data Chunk.

If alpha is true then it writes the Transparency Values in the tRNS Chunk to the PNG file. If it's false it will flatten the background color & pre-process the Transparency Values. It will use a method similar to PaintDotNet. It sets a Transparency Threshold of 128. Any alpha values less than 128 become zero. Any above 128 become 255 (fully opaque). For the new opaque values the corresponding old alpha for each is then pre-multiplied with the RGB values using the Alpha Blend Formula. That ensures that the pixels look correct.

Normally the flattened background has one color which is (0,0,0) Black. But for AROS I would probably change it to grey to match the window background color for pseudo-transparency.

If this all works well there may be a future use for this app.



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #4 on: August 04, 2022, 11:31:27 PM
One ot these is a 32bit PNG Image with Full Transparency.

The other two are 8bit Palette-Mapped PNG Images. One has Simple Transparency with tRNS.

The other 8bit PNG has No Transparency. Can you see the difference ?

The two 8bit PNG Images were produced using Neuquant Algorithm for Color Quantizing.



AMIGASYSTEM

  • Legendary Member
  • *****
    • Posts: 2095
    • Karma: +48/-1
  • AROS One
    • AROS One
Reply #5 on: August 04, 2022, 11:45:38 PM
I would say almost the same, only if you look at them with a magnifying glass you notice the difference, the third one on the right should be the 32bit one


miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #6 on: August 05, 2022, 02:11:03 AM
I would say almost the same, only if you look at them with a magnifying glass you notice the difference, the third one on the right should be the 32bit one

Very good. The one on the far right is 32bit ARGB.

The one in the middle is 8bit using tRNS Chunk.

The one is 8bit with no alpha.



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #7 on: August 08, 2022, 02:40:10 AM
This topic should have been PNG Image Transparency.

My DT2PNG program was already capable of producing an 8bit PNG image from a 32bit image as input. It uses Neuquant32 which is an extension of Anthony Dekker's original Neuquant Algorithm. As the name implies Neuquant32 can handle 32bit data so each color in the Quantized Color Palette is composed of 4 bytes, ARGB.

My challenge, and it is truly a challenge, is to write new code for DT2PNG to produce a 'tRNS' Data Chunk so the output 8bit PNG will show Simple Transparency. In fact, it will look very similar to the original 32bit image.

It's a long, difficult process to produce the tRNS Data. We start by looping through all 256 colors in the Neuquant Std Palette which is simply called 'map'. It is a unsigned char [256] [4] so it supports ARGB data. We have to find the transparent colors for the byte array 'trans' as well as 'num_trans' so we know the count of trans bytes which we will need later. We already have a Std Palette so we need a 'Remap' function to create a 'tRNS Palette' rearranged so that all the colors with transparent values less than 255 come first. All other values have alpha = 255 which means they are opaque. Sounds easy doesn't it?

Here's the code so far. For(x=0; x<n_colors; x++) {
If(map[ x ][ 0]  == 255, remap[ x]  = high_index++;
Else, remap[ x ] = low_index++; }

In this case low_index holds num_trans which is the total count of transparent colors. The tRNS Chunk only contains transparent values not opaque values, hence why we separate high & low index values.

In the next step we can gather values for the 'trans' byte array. For(x=0; x<n_colors; x++) { trans[ remap[ x ] = map[ x] [ 0 ]; }

After that I use remap[ x] in a similar way to get all the colors for the 'tRNS Colormap'. The next step is to find all the tRNS index values now that the colormap has changed. But my remap function doesn't work with Neuquant's inxsearch function so I have to do it differently. I have 2 Colormaps : Std Colormap & tRNS Colormap. I also have the Original Index values from inxsearch. So I need two new functions FindColor using Std Colormap & Std Index value. It returns the Original RGB color from the Original Palette. FindIndex uses the tRNS Colormap & Original Color from FindColor to return the new tRNS Index value. We do that for every index value. Then store the indices in the Datatype. After storing the tRNS Colormap & tRNS Index values we use PNG Datatype to save the 8bit PNG.

But then the fun part. We have to insert the tRNS Chunk.

So much fun!  ;)
« Last Edit: August 08, 2022, 02:59:17 AM by miker1264 »



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #8 on: August 10, 2022, 03:04:39 AM
DT2PNG is almost complete.

I wanted to spend some time with it because much of the code will be included in the next icon app - Icon press.

I have a remap function that remaps the color co-ordinates. So I derived a tRNS Colormap from the Standard Neuquant Colormap.

I was messing around with FindColor & FindIndex to remap the standard index values when I realized I was going about it the wrong way. Sometimes the easiest solution is the most elegant solution. The beauty is in the simplicity.

remap[ x ] holds the remap co-ordinates for each color in the colormap. Such as for( x=0; x<n_colors; x++ ) {
CMAP[ ( x*3 ) ] = r;  CMAP[ ( x*3 ) + 1 ] = g;. CMAP[ ( x*3 ) + 2 ] = b; }  That will provide the standard Colormap.
But we merely substitute remap[ x ] for x. Such as
CMAP[ ( remap[ x ] *3 ) ] = r;
But this produces the tRNS Colormap.
When x is 66 then remap[ x ] = 255. So in the above example instead of CMAP[ 66*3 ] = r; We get CMAP[ 255*3 ] = r; That effectively remaps the index of the current color.

We get the Neuquant index values using inxsearch in
for( x=0; x<n_colors; x++ ) {
buffer [ x ] = inxsearch... }
So buffer[ x ] holds the standard index value. Which also means we can simply remap the index.
buffer[ x ] = remap[ buffer[ x ] ];
Which means when buffer[ x ] = 66 then remap[ 66 ] = 255.
So now the tRNS index values are also correct.

Funny thing though. I was comparing my IDAT image data with that of the same image produced by a different program using different read/write PNG methods. But I couldn't understand why the IDAT data was different & it was 8 bytes smaller in size.

But I realized my image data was just fine. The PNG image displayed perfectly with Transparency with a tRNS Chunk. The difference was my program uses AROS PNG Datatype. There were two different PNG compression schemes so each compressed the image data differently but correctly.

So all is well with my experiment with 8bit PNG with tRNS Chunk. I will finalize it & release it for others to play with it.

It accepts any 32bit image supported by Picture Datatypes. It Quantizes it to 8bit PNG with Colormap & tRNS Chunk. If the original had Transparency so will the 8bit PNG image.

Thanks to @deadwood for pointing out that the AROS PNG Datatype supports tRNS Transparency.

« Last Edit: August 10, 2022, 03:12:12 AM by miker1264 »



miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #9 on: August 10, 2022, 09:23:49 PM
One last hurdle to overcome.

The crc32 checksum for the tRNS Chunk is not working.

I simply need to find the correct PNG checksum formula



magorium

  • Senior Member
  • ****
    • Posts: 459
    • Karma: +54/-0
  • Convicted non contributor
Reply #10 on: August 11, 2022, 01:57:17 AM
@miker1264:
Are you sure it is the checksum formula/routine itself ?

The checksum includes the chunk-type and the actual data (but not the length)., see also: http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.1

For the CRC algorythm  see: http://www.libpng.org/pub/png/spec/1.1/PNG-Structure.html#CRC-algorithm and an example http://www.libpng.org/pub/png/spec/1.1/PNG-CRCAppendix.html

In case not useful then please ignore.


miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #11 on: August 11, 2022, 03:16:31 AM
@miker1264:
Are you sure it is the checksum formula/routine itself ?

The checksum includes the chunk-type and the actual data (but not the length)., see also: http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.1

For the CRC algorythm  see: http://www.libpng.org/pub/png/spec/1.1/PNG-Structure.html#CRC-algorithm and an example http://www.libpng.org/pub/png/spec/1.1/PNG-CRCAppendix.html

In case not useful then please ignore.

@magorium

Yes. These links are useful. I have been educating myself by researching CRC32. I will save the links in my Sample Code directory. Each project has one. I'm trying to get more organized & efficient.

That's what I do when I find something that seems more complicated. I study every aspect of it for a few days, absorbing what I can & taking notes as I go.

Eventually it becomes easier to understand & I can then write the needed function(s) & test. At least then I will know what I'm looking at.  :)




miker1264

  • Legendary Member
  • *****
    • Posts: 1291
    • Karma: +82/-2
Reply #12 on: August 11, 2022, 03:31:33 PM
I think I'm getting the gist of crc32.

It just takes a bit to sort it all out.



AMIGASYSTEM

  • Legendary Member
  • *****
    • Posts: 2095
    • Karma: +48/-1
  • AROS One
    • AROS One
Reply #13 on: August 11, 2022, 03:54:18 PM
Unhurried miker, in Italy we say ... the hasty cat makes blind kittens


magorium

  • Senior Member
  • ****
    • Posts: 459
    • Karma: +54/-0
  • Convicted non contributor
Reply #14 on: August 11, 2022, 05:10:14 PM
I think I'm getting the gist of crc32.

It just takes a bit to sort it all out.
No worries, i'm more than sure you are capable enough to figure it out (and in case not you can always ask for help).


Glad to hear the links were of use to you. thx for letting me know.