[SOLVED] GetPixel of 8 bit Sprite in 32-bit game

Started by eri0o, Fri 05/03/2021 16:42:57

Previous topic - Next topic

eri0o

I have a big matrix I would like to read in-game, and to make it easier to manipulate, I would like to store it as a grayscale bitmap. I need 8 bits, values going from 0 to 255.

Is it possible to do this in a 32 bit game? If I save it as a RGB BMP (24-bit) and import, I only get 5 bit of resolution per channel using GetPixel.

I never imported 8-Bit images and I am not sure if this is possible in a 32-bit color game - there's an option about remapping palettes, I don't understand it.

Monsieur OUXX

#1
You're asking two questions:
1) what are the catch-22s of importing an 8bit image into a 32 bit game? How does it work?  Good question. I haven't done it myself. I'm curious: I would have thought that the kind of matrix you want to manipulate is generated from inside the game? But you're suggesting that you generated it outside. Could you detail your process a little more? maybe you should save it as an actual 32-bit image and do the conservation in the script *after* importing the image?
2) yeah there's only 5 bits per channel, and I think blue even has only 4 bits or something, I can't remember.. Again: I'm not sure how you generated the image in the first place, otherwise I would have suggested you configure two channels to get your precious 8 bits. As in: for example using the range of blues to represent values from 0 to 127 and the range of red to represent values from 128 to 255.

If it can help I have a module to store arrays of ints into dynamic sprites and back. If it works for values 0 to 16,000,000, surely it can work for values 0 to 255. 😉😉 It's slow and awful but it does the job, and it will save you the research time to avoid the caveats of GetPixel.
 

eri0o

#2
1) So from reading the source it appears AGS doesn't set everything it imports to the game color depth. I don't know why I thought it, so I will load a pixel editor and make a small test ramp of colors to confirm this. If this is true then I should just set it . What appears to have confused my is that GetPixel will convert the color to AGS 16bit color system IF the sprite (and not the game!) is using a color depth bigger than 8. So for the specific 8 bit case it should work.

2) It's too much work, the idea is that I can use a software that has the option to export to this specific format. But I was converting to 32 bit before importing because I didn't knew it was possible to import a 8 bit Sprite in a 32 bit color game.

Edit: But, it turns out I can't directly access the pixel from the Sprite, I have to either Create a Dynamic Sprite or put it into the Room Background. The Dynamic sprite becomes a 32 bit color sprite! And the Room Background doesn't have a way for me to read it's color depth, but the resulting range is giving me AGS 16bit colors too (giving me 5 bit only of effective depth going from black to white) instead of 8 bit... It seem I will need to learn how to work with a 8-bit color game entirely if I want this data.

Edit2: Turns out even thought the Sprite import GUI shows that the sprite only have 8bit of depth, as soon it gets imported, the sprite reads 32 bit color depth in the sprite explorer panel. So it gets converted at import it seems both in the Room or in the Sprite import panel.

Edit3: It seem there's no easy way, it appears I will have to pack the data in 5 top most bits of the Red channel and the remaining data in the 6 top most bits of the green channel and later recover from these. I will need to script this in python or something and then import in AGS the resulting 32-bit BMP.

Edit4: Here's the python script:

Code: python
from PIL import Image
import numpy as np

im_in = Image.open("mysterious.png")
width = im_in.width
height = im_in.height
ori_bmp = np.array(im_in).reshape(width,height,4)
res_bmp = np.full((width,height,4), 255, dtype=np.uint8)

for row in range(height):
     for col in range(width):
        ori_color = int((int(ori_bmp[row,col][0])+int(ori_bmp[row,col][1])+int(ori_bmp[row,col][2]))/3)
        red = ((((ori_color >> 3) & 31)) << 3) & 248
        green = ((ori_color & 7) << 2) & 252
        res_bmp[row,col][0] = red
        res_bmp[row,col][1] = green
        res_bmp[row,col][2] = 0
        res_bmp[row,col][3] = 255
        
im_out = Image.fromarray(res_bmp, "RGBA")
im_out.save("output.png", "png")


Then on your game, read the color like this:

Code: ags
int color = surf.GetPixel(i%surf.Width, i/surf.Width);
int col_r = (color & 63488) >> 11;
int col_g = (color & 2016) >> 5;
int col_b = (color & 31);
col_r = (col_r << 3) & 255;
int final_col = col_r + col_g;

Crimson Wizard

#3
Quote from: eri0o on Fri 05/03/2021 23:03:06
Edit2: Turns out even thought the Sprite import GUI shows that the sprite only have 8bit of depth, as soon it gets imported, the sprite reads 32 bit color depth in the sprite explorer panel. So it gets converted at import it seems both in the Room or in the Sprite import panel.

This is curious, I am 100% certain this was possible in old versions, but maybe something changed (this was also a source of endless graphic bugs and confusion when using old templates with 8 and 16-bit sprites too).
Perhaps a use-case like described here is valid and enough reason to provide such option.

DynamicSprite and DrawingSurface are supposed to take color depth of the original image.

Quote from: eri0o on Fri 05/03/2021 23:03:06
Edit: But, it turns out I can't directly access the pixel from the Sprite,

This returns us to the ancient feature request. One of the potential solutions is to implement readonly DrawingSurface only for pixel-picking.
In the long run GetPixel is slow, because it does function call for each pixel and range checks too, so perhaps planning some api for read/write access to pixel array would be a good idea too.

eri0o

Quote from: Crimson Wizard on Sat 06/03/2021 02:00:32This returns us to the ancient feature request. One of the potential solutions is to implement readonly DrawingSurface only for pixel-picking.
In the long run GetPixel is slow, because it does function call for each pixel and range checks too, so perhaps planning some api for read/write access to pixel array would be a good idea too.

My image was 1024x1024, so what I did is made a static array (I couldn't make it dynamically) and then I used rep exec always to gradually read it's pixels and fill in the array while other things are happening so when it comes the time I just access directly in the array.

Of course having direct array access would be great but doing like this is how I worked around it.

SMF spam blocked by CleanTalk