It is currently Mon Dec 11, 2017 11:40 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Sat Mar 11, 2017 2:19 pm 
Offline
User avatar

Joined: Mon Oct 20, 2014 1:50 pm
Posts: 94
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?

_________________


Top
 Profile  
 
PostPosted: Sat Mar 11, 2017 2:33 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5891
Location: Canada
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.


Last edited by rainwarrior on Sat Mar 11, 2017 3:08 pm, edited 3 times in total.

Top
 Profile  
 
PostPosted: Sat Mar 11, 2017 2:36 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6509
Location: Seattle
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


Top
 Profile  
 
PostPosted: Sat Mar 11, 2017 2:43 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5891
Location: Canada
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.


Top
 Profile  
 
PostPosted: Sat Mar 11, 2017 3:44 pm 
Offline
User avatar

Joined: Mon Oct 20, 2014 1:50 pm
Posts: 94
@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.

_________________


Top
 Profile  
 
PostPosted: Wed Apr 12, 2017 4:51 am 
Offline

Joined: Sun Apr 02, 2017 12:56 am
Posts: 3
Is there any other way to do so because i m getting invalid adjustment error!
HyperthymesiaRemedies


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group