DizzyThermal Posted January 17 Posted January 17 Hello! I'm trying to understand a binary format of an image transparency mask for NexusTK - a 2D MMORPG from 1996. (Disclaimer: I received help back in the XeNTaX days, and no longer have a way to contact who helped, so my understanding of this format is not great - that's why I'm asking here ). To build my transparency masks, I use the following code - it's GDScript so the code is python-ish: var STENCIL_MASK := 0x80 var mask_image := Image.create(width, height, false, Image.FORMAT_RGBA8) for i in range(height): var total_pixels := 0 while true: var pixel_count := read_u8() if pixel_count == 0x0: break # End of mask var should_draw := false if pixel_count > STENCIL_MASK: should_draw = true if should_draw: # Not sure why this works pixel_count = pixel_count ^ STENCIL_MASK for j in range(pixel_count): var pixel_color := Color.TRANSPARENT if should_draw: pixel_color = Color.BLACK mask_image.set_pixel(total_pixels, i, pixel_color) total_pixels += 1 if total_pixels < width: for j in range(width - total_pixels): mask_image.set_pixel(total_pixels, i, Color.TRANSPARENT) An example mask and image are: The mask is used to blit out the background pixels that aren't suppose to be rendered (pink pixels above). Is this image mask format common or custom (like I'm not sure why 0x80 is a special value)? Is there a better way I can build this transparency mask, instead of pixel by pixel? Thanks! -DizzyThermal
Solution piken Posted January 19 Solution Posted January 19 (edited) > no longer have a way to contact who helped Greetings again Dizzy. > Is this image mask format common ... Yes. RLE byte masks using the high-bit for the opacity and lower 7 bits for the run length count are pretty common (I've seen them a few times before). > Not sure why this works Because you're effectively masking off bits to just get the count, like you said x & 0x7F or x - 128. > I'm not sure why 0x80 is a special value A game could have arbitrarily chosen to use the low bit (0x01) instead and store the run in the upper bits (needing a >> 1), but using the high bit is easy to test and just requires a mask of 0x7F to get the count. > Is there a better way I can build this transparency mask, instead of pixel by pixel? Shrug, I know 0% about GDScript, but I can say that often "set pixel" functions on various image classes can be slower than desired, and that it's often efficient to supply all the data at once from a byte array. I see there's a set_data function, which might be faster, but measure your performance if you care ⏱️. Edited January 19 by piken 2
DizzyThermal Posted January 26 Author Posted January 26 RLE is indeed what this is! I just need to figure out how to hand the whole bytearray off to set_data function instead of determining and setting each pixel manually. I'll follow up with my timing results when I can. Thanks again piken, and hello again!
DizzyThermal Posted January 26 Author Posted January 26 (edited) I tried to build a PackedByteArray and create the image from that, but since I have to still go pixel-by-pixel as I decode the RLE to build it up I'm not sure I'm saving much time.. However, I think it's more readable, so I'll keep this iteration: var STENCIL_MASK := 0x80 var mask_byte_array := PackedByteArray() mask_byte_array.resize(width * height * 4) var byte_offset := 0 for i in range(height): var total_pixels := 0 while true: var pixel_count := read_u8() if pixel_count == 0x0: break var should_draw := false if pixel_count > STENCIL_MASK: should_draw = true if should_draw: pixel_count = pixel_count ^ STENCIL_MASK var pixel_color := Color.BLACK if should_draw else Color.TRANSPARENT for j in range(pixel_count): mask_byte_array.encode_u32(byte_offset, pixel_color.to_abgr32()) byte_offset += 4 total_pixels += 1 if total_pixels < width: for j in range(width - total_pixels): mask_byte_array.encode_u32(byte_offset, Color.TRANSPARENT.to_abgr32()) byte_offset += 4 var mask_image := Image.create_from_data(width, height, false, Image.FORMAT_RGBA8, mask_byte_array) I didn't know this was RLE and that there was an actual format for it, thanks for the knowledge Edited January 26 by DizzyThermal Add new RLE decoding algorithm
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now