nesdev.com
https://forums.nesdev.com/

Converting 2bpp tiles to 8bpp image
https://forums.nesdev.com/viewtopic.php?f=21&t=15657
Page 1 of 1

Author:  DragonDePlatino [ Sat Mar 11, 2017 2:19 pm ]
Post subject:  Converting 2bpp tiles to 8bpp image

I'm writing a ROM-editing tool in C and need to extract some 8x8 tiles from CHR-ROM. First, I want to convert 2pp NES tiles to indexed 8bpp images. Second, I want to render them to an OpenGL canvas so the user can edit them. I haven't learned a lick of OpenGL yet but I just managed to wrap my head around the conversion part. This is my code:
Code:
typedef unsigned char byte;

/* Get 16 bytes from somewhere in CHR-ROM. */
byte* data = rom_data(0x040010, 0x10);

/* 8bpp image that will hold tile. */
byte image[64];

int pixel = 0;
byte* low_bit = data;
byte* high_bit = data + 8;
int row = 0;
while(pixel < 64) {
   image[pixel++] = (bool)(low_bit[row] & 0b10000000) + (bool)(high_bit[row] & 0b10000000) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b01000000) + (bool)(high_bit[row] & 0b01000000) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00100000) + (bool)(high_bit[row] & 0b00100000) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00010000) + (bool)(high_bit[row] & 0b00010000) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00001000) + (bool)(high_bit[row] & 0b00001000) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00000100) + (bool)(high_bit[row] & 0b00000100) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00000010) + (bool)(high_bit[row] & 0b00000010) * 2;
   image[pixel++] = (bool)(low_bit[row] & 0b00000001) + (bool)(high_bit[row] & 0b00000001) * 2;
   ++row;
}

/* Print out 8bpp image pixel-by-pixel to validate. */
for (int y = 0; y < 8; ++y) {
   for (int x = 0; x < 8; ++x) {
      printf("%d ", image[x + y * 8]);
   }
   putchar('\n');
}


So this is my input and output:
Image
Code:
0 0 0 0 0 0 0 0
0 3 3 3 1 1 1 0
0 3 3 3 1 1 0 0
0 3 3 3 1 0 0 0
0 2 2 2 0 0 0 0
0 2 2 0 0 0 0 0
0 2 0 0 0 0 0 0
0 0 0 0 0 0 0 0

I have two questions:
  1. Is there a faster, less-verbose way to do this in C? All of this casting, bitmasking and multiplication feels unecessary.
  2. Is it even possible to display indexed 8bpp images in OpenGL? Will that be hardware-accelerated? Or will I need to use a shader and 24bpp image?

Author:  rainwarrior [ Sat Mar 11, 2017 2:33 pm ]
Post subject:  Re: Converting 2bpp tiles to 8bpp image

You can do the bitmasking in a for-loop by treating the tile bytes as shift registers. Something like:
Code:
// tile[16] input 16 byte CHR tile data
// output[8][8] output image

for (y=0; y<8; ++y)
{
   a = tile[y+0];
   b = tile[y+8];
   for (x=0; x<8; ++x)
   {
      output[y][x] = ((b & 0x80) >> 6) | ((a & 0x80) >> 7);
      a <<= 1;
      b <<= 1;
   }
}

An aggressive optimizing compiler might even be able to fully unroll this.

Edit: was off by 1 on the shifts.

Author:  lidnariq [ Sat Mar 11, 2017 2:36 pm ]
Post subject:  Re: Converting 2bpp tiles to 8bpp image

DragonDePlatino wrote:
Is there a faster, less-verbose way to do this in C? All of this casting, bitmasking and multiplication feels unecessary.
It's verbose because you're unrolling your loop.
Compare it to my chr2pgm...

Also you might find something useful in the bit twiddling hacks

Author:  rainwarrior [ Sat Mar 11, 2017 2:43 pm ]
Post subject:  Re: Converting 2bpp tiles to 8bpp image

Alternative to my previous illustration, you don't even have to use them like shift registers if you just use indexed bit shifts:
Code:
for (y=0; y<8; ++y)
{
   a = tile[y+0];
   b = tile[y+8];
   for  (x=0; x<8; ++x)
   {
      output[y][x] = (((b >> (7-x)) & 1) << 1) | ((a >> (7-x)) & 1);
   }
}

This is probably even easier for a compiler to optimize.

Edit: I was curious so I stuck both examples into the godbolt compiler explorer. I'm kinda surprised, but GCC unrolls the shift-register approach better? Maybe? The practical difference might be marginal, though.

Author:  DragonDePlatino [ Sat Mar 11, 2017 3:44 pm ]
Post subject:  Re: Converting 2bpp tiles to 8bpp image

@rainwarrior
Great answer. It took me a minute to digest it (and I had to rotate your output 90 degrees) but it's a very elegant solution. And your hunch was correct. The shift-register approach is approximately 4% faster. Thanks for answering my first question.

@lidnariq
Yikes. I actually came across that standford page while looking for a solution. There's some good stuff in there but it's an overwhelming amount of information! This chr2pgm is a good reference, though.

Author:  martin123 [ Wed Apr 12, 2017 4:51 am ]
Post subject:  Re: Converting 2bpp tiles to 8bpp image

Is there any other way to do so because i m getting invalid adjustment error!
HyperthymesiaRemedies

Page 1 of 1 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/