TricksterGuy / nin10kit

(Formerly brandontools) A set of tools for doing homebrew game development, includes look up table generators and and image to C exporter for Nintendo's Gameboy Advance, DS, and 3DS systems. In addition the GUI version of the program allows you to see what the image would look like on real hardware before exporting. Coming soon the ability to edit the bitmap/palette/tilemaps before exporting to the GBA.
Apache License 2.0
49 stars 3 forks source link

Restricted palette images incorrectly exported #32

Open HorstBaerbel opened 5 years ago

HorstBaerbel commented 5 years ago

I have an 1024x1024 image with 191 colors (RGB->indexed in Gimp, Gimp color analysis tells me 191 colors when loading the PNG). When I export with the GUI in mode 4 I get a 161-color palette, when I export with "nin10kit --mode=4 --bpp=8 foo image.png" it get a 159-color palette. Using latest git version.

HorstBaerbel commented 5 years ago

I have another one here that has 256 colors and is output with a 32 color palette when converted from the GUI...

TricksterGuy commented 5 years ago

Alright now that I am on break now I can start addressing all these. Again thanks for the bug reports.

As far as gimp goes, that's more than likely expected since its operating with each pixel being 24 bits. The concern here is the discrepancy between the GUI and CLI which I don't know off the top of my head why the palette's generated differ. I'd probably side with the CLI here since the GUI was a last minute thing thrown on to appease students.

HorstBaerbel commented 5 years ago

I don't believe GIMP is lying to me. I would be pretty crappy if it would... I converted the image to 191 colors just a moment before. Also ImageMagicks identify says:

color.png PNG 1024x1024 1024x1024+0+0 8-bit sRGB 191c 674KB 0.000u 0:00.000

so I guess nin10kit doing something weird.

HorstBaerbel commented 5 years ago

Ok. So height.png was apparently still 16-bit, but converting it to 256 colors and running nin10kit again does not change the outcome... GIMP says it has 256 color and identify tells me:

height.png PNG 1024x1024 1024x1024+0+0 8-bit sRGB 256c 253KB 0.000u 0:00.000

HorstBaerbel commented 5 years ago

From what I grasp from a quick debugging session is that:

TricksterGuy commented 5 years ago

Right so I should mention what actually happens when you run the tool on a set of images.

Anywho for mode4/tiled exports essentially

  1. The image is read in via ImageMagick

  2. The image pixel data is converted to 16 bit color

  3. What happens at that point depends on how many colors are in the image.

If the image has <= the number of colors for that mode then the palette will simply be what it finds

If the image has more colors than the mode supports, then the median cut algorithm is ran to reduce it to that number of colors for you, building a palette that would work for all of the images.

I'd like to remind you that the program's userbase is almost entirely college students. Most aren't artists and will just probably just grab random images off the internet, meaning I'm expecting most images used to have over 256 colors which the program would handle for you. If someone doesn't like the result then gimp can be used beforehand.

And yeah ideally the user should either 1) Give me a palette along with the images, or ensure that all images don't have more than 16/256 colors. Writing an implementation for 4 bpp was a pain in the butt...

All that said, I'm open to adding more options and having fine-tuned configurations (and defaults) based on who is using the program so that people who know what they are doing won't have to pass in --force all the time. It wouldn't be a hard thing to do at all.

TricksterGuy commented 5 years ago

And yeah thanks for pointing out those issues, I think having more documentation and logging to let you know what is going on would help here.

HorstBaerbel commented 5 years ago

If the image has <= the number of colors for that mode then the palette will simply be what it finds

It does not seem to do this. I've stepped through the code. It always dithers. Even if there's no reason to do so. Even if .dither is false it does this... I'd fix it, but I'm not yet familiar with the code and a bit cautious not break anything. Shall I try to take a stab at this? I can send you the pull request an you can adjust it if needed.

I'd like to remind you that the program's userbase is almost entirely college students.

To write code that draws an image on the GBA (or NDS nowadays) they must necessarily know what bitdepth to use. Imo better throw an error and explain them why and how to correct it.

TricksterGuy commented 5 years ago

Yeah I'll take care of the dither issue later today (its 6am and I haven't slept). and its a quick fix. I've been setting up a new computer.

And well most students aren't going to put in the effort to edit an image and get it below the number of colors threshold. The result if an error was thrown would be mainly bland looking games with a few colored shapes. In addition we didn't explain any image editors or anything, just the steps on how to draw things in the various modes (but nowadays they only cover mode 3) given that you had the image data / palette / etc.

The assignments were just open ended, write a game using mode 3, mode 4, etc. So yeah the bitdepth is a known thing.

But yeah open to adding that failsafe to not try to reduce colors.

TricksterGuy commented 5 years ago

So lets back up for a second here. and go back to your initial query.

The steps you are taking are converting the image into indexed color in gimp, I see that it indeed has 256 colors. You then use the tool to export it and find that it has less colors than in gimp.

Is that correct?

If that's the case is this image already a 16 bit image? Looking at the pixel data it doesn't seem to be.

Now I convert the image to indexed color using gimp and dumped the palette. I'm seeing entries in the palette GIMP formed that are very close to each other for instance (41 49 45 255), (45 51 43 255) these palette entries from gimp would map to the same color since the bottom 3 bits are truncated to do the 16 bit conversion.

So its as I said initially.

Whats happening is when you convert via GIMP to indexed color you lose some color info, that palette formed is still 8 bits per color channel. And then when you export via nin10kit it takes the colors in that image and converts it to 16 bit color with 5 bits per color channel, you lose even more colors since entries in the palette GIMP made will map to each other, this explains why you are seeing 191 colors rather than the 255 GIMP gave you.

Its better to just let the program do its thing, since it is uses the same algorithm that GIMP uses, but taking advantage of the constraint that we only work with 16 bit colors. Exporting the original image without first converting it to indexed color will result in a palette of 255 colors.

TricksterGuy commented 5 years ago

I suppose the same analysis applies for the second image you posted as well.

I did take a look at the dither thing, yes it will still call the dithering functions. However the dither flag is plumbed all the way down and will effectively not have an effect if set to false.

See: https://github.com/TricksterGuy/nin10kit/blob/master/shared/dither.cpp#L42

Yeah totally could have been better written to call any of the dithering functions if the flag is false. Blame 2015 me for that...

HorstBaerbel commented 5 years ago

Oh yeah, sure. You're right about the 5 bit color thing! That sounds pausible! I looked through the options and used:

--mode=4 --bpp=8 --start 1 --palette 191

And indeed I get 191 colors + an empty color 0. Just what I wanted (and much more convenient that doing it in GIMP)! I guess I was doing it wrong all along... Not sure why the CLI and GUI versions are different though. That should maybe be resolved.

HorstBaerbel commented 5 years ago

The problem with the greyscale image / height map is not resolved though. This is essentially a uin8_t image. I want nin10kit not to fumble with it. The color values might not be different in 5bit, but I don't care. I just want the RAW data. I don't even need a palette... Should I make a feature request for a new flag "--raw" or "--pass-through" or so?

TricksterGuy commented 5 years ago

Yeah its not doable currently because any of the export options will fumble with the image to convert it, shouldn't be too hard to add though.

HorstBaerbel commented 5 years ago

See #34. Converting the height map with "--mode=direct" does what I want. I also tried the color map and some 4bpp and 24bpp images.