- Flipping RLET-Encoded Sprites
- 17 Nov 2021 02:38:15 pm
- Last edited by King Dub Dub on 19 Nov 2021 09:04:18 am; edited 1 time in total
Heller fellas, I've been trying to figure out how to flip RLET sprites since converting them to regular sprites, flipping them, and converting them back to RLET sprites is slow and inefficient. I managed to reverse engineer the format create a function to flip it across the horizontal axis:
Code:
It's works and doesn't make me want to stab forks in my eyes, but I wanted to ask if there were any optimizations I could make. I also made a function for finding the size:
Code:
Again, any optimizations or possible bugs? I'm working on the vertical flipping now, and my logic is to paste the first transparent run on the end of the row (the length/offset of which I find beforehand) if it isn't zero, copy the non-transparent data over, and then make sure the first byte of the output row is zero if there aren't any transparent runs at the end of the input. It works in my head until I think about it for too long, so I'll see what I can do. Hopefully this'll help someone in the future!
Code:
//Takes a RLET-encoded sprite and flips it across the X-axis
void horizontal_flip_rletsprite(const gfx_rletsprite_t *input, gfx_rletsprite_t *output, const uint24_t size)
{
output->width = input->width;
output->height = input-> height;
uint24_t data_offset = 0;
//the input size has the first 2 bytes for height and width, we only need the data:
uint24_t data_size = size - 2;
for(uint8_t y = 0; y < input->height; ++y)
{
//reset the line size (in bytes, not pixels):
uint8_t row_size = 0;
//figure out the row size:
for(uint8_t x = 0; x < input->width;)
{
//grab the transparent run length and increment sizeof afterwards:
x += input->data[data_offset + row_size++];
//make sure the last transparent run wasn't the whole line:
if(x < input->width)
{
//the next byte is number of non-transparent colors:
x += input->data[data_offset + row_size];
//the next byte is the number of non-transparent colors, add that plus 1 for the next run:
row_size += (input->data[data_offset + row_size] + 1);
}
}
//copy the first row of the input to what will be the last row of the output:
memcpy(&output->data[data_size - row_size - data_offset], &input->data[data_offset], row_size);
data_offset += row_size;
}
}
It's works and doesn't make me want to stab forks in my eyes, but I wanted to ask if there were any optimizations I could make. I also made a function for finding the size:
Code:
//Gets the data size of an RLET-encoded sprite, good for allocating memory for flipped copies
uint24_t get_rlet_size(const gfx_rletsprite_t *input)
{
//optimizing for speed here:
uint24_t width = input->width;
uint24_t height = input->height;
uint24_t data_offset = 0;
for(uint8_t y = 0; y < height; ++y)
{
//figure out the row size:
for(uint8_t x = 0; x < width;)
{
//grab the transparent run length and increment sizeof afterwards:
x += input->data[data_offset++];
//make sure the last transparent run wasn't the whole line:
if(x < width)
{
//the next byte is number of non-transparent colors:
x += input->data[data_offset];
//the next byte is the number of non-transparent colors, add that plus 1 for the next run:
data_offset += (input->data[data_offset] + 1);
}
}
}
//add the two width and height values:
return data_offset + 2;
}
Again, any optimizations or possible bugs? I'm working on the vertical flipping now, and my logic is to paste the first transparent run on the end of the row (the length/offset of which I find beforehand) if it isn't zero, copy the non-transparent data over, and then make sure the first byte of the output row is zero if there aren't any transparent runs at the end of the input. It works in my head until I think about it for too long, so I'll see what I can do. Hopefully this'll help someone in the future!