## Sprite Graphics

Share and discuss all facets of DKC ROM hacking...

### Re: Sprite Graphics

On this,
Code: Select all
if ((a & bit) == 0) { ...

Be careful of doing this:
Code: Select all
if (a & bit == 0) { ...

These mean entirely different things! In the first scenario, you are checking if the result of 'a & bit' equals 0. In the second, you are checking if bit equals 0, then ANDing that with a
Treasure Hunter
93
Posts: 495
Joined: 2016

### Re: Sprite Graphics

Ah, you are correct there. == has higher precedence than & which has higher precedence than &&. I think I was getting mixed up with the last two.

Code: Select all
if ( a & b  &&  c & d ) { ...
if ((a & b) && (c & d)) { ...

if ( a == b  &&  c == d ) { ...
if ((a == b) && (c == d)) { ...

if (a &  b == c ) { ..
if (a & (b == c)) { ..

if (a &&  b == c ) { ..
if (a && (b == c)) { ..

rainbowsprinklez wrote:Rather than remembering the precedence, I just always use parentheses!

Yeah, it's a much safer way to do things and it clearly conveys the programmer's intent. The only downside is that you can sometimes end up with a lot of parentheses, but even that's not all that bad and there are still ways to make them look pretty.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:Yeah, it's a much safer way to do things and it clearly conveys the programmer's intent. The only downside is that you can sometimes end up with a lot of parentheses, but even that's not all that bad and there are still ways to make them look pretty.

Here is one line of code I have:
Code: Select all
int g = ((raw >> 5) & 0x1f) << 3;

Not bad, by any means, but an example of how I'm too lazy to check precedence rules. I'm sure I can remove ONE pair of parentheses

Guess what this line is part of, Kingizor! I imagine it is pretty obvious to someone as experienced as you!
Treasure Hunter
93
Posts: 495
Joined: 2016

### Re: Sprite Graphics

Shifts have higher precedence than AND, so I don't think you can reduce that as-is without also changing the value you're ANDing with. Without that limitation you could reduce it to:

Code: Select all
int g = (raw >> 2) & 0xF8;
int g =  raw >> 2  & 0xF8;

Compilers for compiled languages should be able to recognise that they are the same and produce identical code. MSVC Interestingly doesn't seem to do that in advance for C# despite doing it for other languages (link), but one instruction isn't going to make much of a difference. As for what that lines does...
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:As for what that lines does...

Come on, man! Come to think of it, I don't think I'm being 100% accurate (as far as the generally accepted way to do this is. Still good enough by SNES standards) This is color. I'm leaving the lowest 3 bits empty, so my "white" is really 248x248x248. Wasn't there some generally accepted way to convert? But, my way doesn't matter, the SNES loses those bits anyways
Treasure Hunter
93
Posts: 495
Joined: 2016

### Re: Sprite Graphics

Got some better code now. thanks for the tips.

2Bpp
Code: Select all
int main()
{
int Bitplane0_ROW[] = { 0b01100110 , 0b11111111, 0b01011010, 0b01111110, 0b00000000, 0b10000001, 0b11111111, 0b01111110 }; // Array to to store numbers Last Row is first.
int Bitplane1_ROW[] = { 0b01111110, 0b11111111, 0b11111111, 0b11011011, 0b11111111, 0b01111110, 0b00000000, 0b00000000 };

int N = 7; //to store bit
int c = 0;

BYTE* buf = new BYTE[8 * 5];

for (int p = 0; p < 8; p++)
{
for (int j = 0; j < 8; j++) // Row 6
{
if ((Bitplane0_ROW[p] & (1 << N)) && (Bitplane1_ROW[p] & (1 << N)) == 0)
{

// Index 1 (Green)
buf[c + 0] = (BYTE)53;
buf[c + 1] = (BYTE)189;
buf[c + 2] = (BYTE)104;

}
else if ((Bitplane0_ROW[p] & (1 << N)) == 0 && (Bitplane1_ROW[p] & (1 << N)) == 0)
{

// Index 0 (White)
buf[c + 0] = (BYTE)255;
buf[c + 1] = (BYTE)255;
buf[c + 2] = (BYTE)255;

}
else if ((Bitplane0_ROW[p] & (1 << N)) == 0 && (Bitplane1_ROW[p] & (1 << N)))
{

// Index 2 (Brown)
buf[c + 0] = (BYTE)59;
buf[c + 1] = (BYTE)85;
buf[c + 2] = (BYTE)142;
}
else if ((Bitplane0_ROW[p] & (1 << N)) && (Bitplane1_ROW[p] & (1 << N)))
{

// Index 3 (Tan)
buf[c + 0] = (BYTE)154;
buf[c + 1] = (BYTE)194;
buf[c + 2] = (BYTE)237;
}
c += 3;
N--;
}
N = 7;
}

SaveBitmapToFile((BYTE*)buf, 8, 8, 24, 0, "C:\\Users\\Chris\\Desktop\\Link_Sprite.bmp");

delete[] buf;
return 0;
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

rainbowsprinklez wrote:
Kingizor wrote:As for what that lines does...

Come on, man! Come to think of it, I don't think I'm being 100% accurate (as far as the generally accepted way to do this is. Still good enough by SNES standards) This is color. I'm leaving the lowest 3 bits empty, so my "white" is really 248x248x248. Wasn't there some generally accepted way to convert? But, my way doesn't matter, the SNES loses those bits anyways

The easiest way to convert the 5-bit value to an 8-bit value is indeed to shift it left by 3. As you've pointed out, by doing it that way the highest 5-bit colour (31) doesn't become the highest 8-bit colour (255). The difference isn't very noticeable most of the time so many tools are quite happy not to go any further.

Strictly speaking, we'd want to imitate the output of the SNES as well as we can, but that's easier said than done. Measuring colour output on a display can approximate it but there are a lot of varying factors that make it imprecise. We can presume that it would be linear but that's not necessarily the case. It definitely doesn't lose the lowest three bits!

Borrowing the algorithm from here, here is a program to produce a linear table of values:

Code: Select all
#include <stdio.h>

int main (void) {
for (int i = 0; i < (1 << 5); i++) {
printf("%3d, ", i * (255 / ((1 << 5) - 1)));

if ((i & 7) == 7)
printf("\n");
}

return 0;
}

That produces the linear sequence:

Code: Select all
0,   8,  16,  24,  32,  40,  48,  56,
64,  72,  80,  88,  96, 104, 112, 120,
128, 136, 144, 152, 160, 168, 176, 184,
192, 200, 208, 216, 224, 232, 240, 248,

There is a tiny tweak that can be made to produce a more even spread. If we change the division to use floating point, it won't be rounded and will maintain the spread. The shortest way to do that in the above program is to change "255" to "255.0". That gives us:

Code: Select all
0,   8,  16,  24,  32,  41,  49,  57,
65,  74,  82,  90,  98, 106, 115, 123,
131, 139, 148, 156, 164, 172, 180, 189,
197, 205, 213, 222, 230, 238, 246, 255,

Which might not necessarily be correct either, but the spread certainly looks a lot better.

Some tools and emulators will have their own exotic (albeit made up) ways of doing this too.

Also bear in mind that 8-bit per channel RGB isn't universal despite being the most common format we'll encounter on PCs. Higher end monitors capable of 10-bit aren't unusual, and there are many displays, particularly TVs, that clamp 8-bit RGB into smaller ranges, e.g. 15-240. Then you've got graphics cards drivers that can enable subsampling without the user realising it. The PNG image format supports up to 16-bit per channel RGB, although famously almost nothing supports it.

The SNES itself doesn't always output RGB either, it depends what kinds of cable you've got plugged in!
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Cyclone wrote:Got some better code now. thanks for the tips.

You seem to have an extra if hiding in there!

This version is a tiny bit farther from what we want compared to the previous version though.

You know that (Bitplane0_ROW[p] & (1 << N)) and (Bitplane0_ROW[p] & (1 << N)) get us the two bits that form an index, so by combining them you would only have to deal a single value after that. What we're aiming towards is that you can use bitwise OR and shifts to combine multiple bits into single values. In fact, you wouldn't need a single if/else at all.

Code: Select all
int a = 0 | (0 << 1); // 0
int b = 1 | (0 << 1); // 1
int c = 0 | (1 << 1); // 2
int d = 1 | (1 << 1); // 3

When we have a value 0-3, we would typically use it as an index for a array.

Code: Select all
unsigned char 2bpp_grey[] = { 0, 85, 170, 255 };

buf[c+0] = 2bpp_grey[index];
buf[c+1] = 2bpp_grey[index];
buf[c+2] = 2bpp_grey[index];

(warning: this particular table has four values, so we should only ever access it with an index of 0 to 3, if we access out of bounds bad things will happen)

Arrays can be constructed at runtime if needed. That's typically what you would do when decoding colour palettes, but since there are red, green and blue components you'd either need three tables or a table containing three values.

Different languages have different semantics for this kind of thing too. I'm not sure if there is a more canonical way to do it in modern C++.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:
Cyclone wrote:Got some better code now. thanks for the tips.

You seem to have an extra if hiding in there!

This version is a tiny bit farther from what we want compared to the previous version though.

You know that (Bitplane0_ROW[p] & (1 << N)) and (Bitplane0_ROW[p] & (1 << N)) get us the two bits that form an index, so by combining them you would only have to deal a single value after that. What we're aiming towards is that you can use bitwise OR and shifts to combine multiple bits into single values. In fact, you wouldn't need a single if/else at all.

Code: Select all
int a = 0 | (0 << 1); // 0
int b = 1 | (0 << 1); // 1
int c = 0 | (1 << 1); // 2
int d = 1 | (1 << 1); // 3

I have an extra if?

I'm a little confused on what this -->Means
Code: Select all
int b = 1 | (0 << 1); // 1

How do I change it to work with my example?
I am trying to understand bitwise operators from this link...

https://www.geeksforgeeks.org/bitwise-o ... -in-c-cpp/
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

https://stackoverflow.com/questions/331 ... eger-value

with this example.
Code: Select all
#include <stdio.h>
#include <stdlib.h>

int main(void){
int LeastSignificantBit=1;
int MiddleBit=0;
int MostSignificantBit=1;
int Number=0;
Number=(MostSignificantBit << 2) + (MiddleBit << 1) + (LeastSignificantBit << 0);
printf("Number is %d\n",Number);
return 0;
}

EDIT Is this even close?
b = (Bitplane0_ROW[p] << 1) | (Bitplane1_ROW[p] << 0);
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

This works
Code: Select all
int bit1 = 1;
int bit2 = 1;

int combined = bit1 | (bit2 << 1);
std::cout << combined << "\n"; // prints 3

but... how do I combine these two → ((Bitplane0_ROW[p] & (1 << N)) && (Bitplane1_ROW[p] & (1 << N)) == 0)
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

We put individual bits at the correct places to form a value.

An important point is that powers of two are each made up of a single bit at different positions:

Code: Select all
0x01 ==   1 == 0b00000001
0x02 ==   2 == 0b00000010
0x04 ==   4 == 0b00000100
0x08 ==   8 == 0b00001000
0x10 ==  16 == 0b00010000
0x20 ==  32 == 0b00100000
0x40 ==  64 == 0b01000000
0x80 == 128 == 0b10000000

If we take say a 1 and shift it left once, we get a 2. Shift it left again and we get a 4.

In your bitplane example, you're extracting a single bit by ANDing against (1 << N) and therefore getting one of these values. That expression leaves you with value with a single bit. To get it where you want it to go, you could either shift it right a variable amount, or force it to 1 with !! and shift it left a fixed amount.

That stackoverflow answer isn't wrong, but it's generally better to use bitwise OR "|" than addition "+" for this. If you're only dealing with single bits they'll behave identically for simple cases, but it's better to use the one that actually conveys what you're doing. That said, when dealing with single bits it is useful to think of them as adding powers of 2 to create new values.

If we have 0x47, we could say it has 0x40 = 64, plus 0x4 = 4, plus 0x02 = 2, plus 0x01 = 1, so 64 + 4 + 2 + 1 = 71

Cyclone wrote: int b = 1 | (0 << 1); // 1

That one is to point out that a 0 doesn't have any bits set, so shifting it or ORing it won't result in any bits getting set (that's fine).

Cyclone wrote:EDIT Is this even close?
b = (Bitplane0_ROW[p] << 1) | (Bitplane1_ROW[p] << 0);

Yes, that's exactly what we want. The only issue is that shifting left by zero won't do anything, so you can safely remove that shift.

At any rate, you're making progress.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:
Cyclone wrote:EDIT Is this even close?
b = (Bitplane0_ROW[p] << 1) | (Bitplane1_ROW[p] << 0);

Yes, that's exactly what we want. The only issue is that shifting left by zero won't do anything, so you can safely remove that shift.

That didn't work when I tried that.
however this worked, I was able to get the index value with this.
Code: Select all
int Index = ((Bitplane0_ROW[p] & (1 << N)) >> N) | (((Bitplane1_ROW[p] & (1 << N)) >> N) << 1);

So now I have...
Code: Select all
int main()
{

int Bitplane0_ROW[] = { 0b01100110 , 0b11111111, 0b01011010, 0b01111110, 0b00000000, 0b10000001, 0b11111111, 0b01111110 }; // Array to to store numbers Last Row is first.
int Bitplane1_ROW[] = { 0b01111110, 0b11111111, 0b11111111, 0b11011011, 0b11111111, 0b01111110, 0b00000000, 0b00000000 };

int N = 7; //to store bit
int c = 0;

BYTE* buf = new BYTE[8 * 5];

unsigned char White[] = {255, 255, 255};
unsigned char Green[] = {53, 189,104 };
unsigned char Brown[] = {59,85,142 };
unsigned char Tan[] = {154,194,237 };

for (int p = 0; p < 8; p++)
{
for (int j = 0; j < 8; j++) // Row 6
{
int Index = ((Bitplane0_ROW[p] & (1 << N)) >> N) | (((Bitplane1_ROW[p] & (1 << N)) >> N) << 1);
if(Index == 0)
{

// Index 0 (White)
buf[c + 0] = White[Index];
buf[c + 1] = White[Index + 1];
buf[c + 2] = White[Index + 2];

}

if (Index == 1)
{
// Index 1 (Green)
buf[c + 0] = Green[Index - 1];
buf[c + 1] = Green[Index];
buf[c + 2] = Green[Index + 1];

}
else if (Index == 2)
{

// Index 2 (Brown)
buf[c + 0] = Brown[Index-2];
buf[c + 1] = Brown[Index -1];
buf[c + 2] = Brown[Index];
}
else if (Index == 3)
{

// Index 3 (Tan)
buf[c + 0] = Tan[Index - 3];
buf[c + 1] = Tan[Index - 2];
buf[c + 2] = Tan[Index - 1];
}
c += 3;
N--;
}
N = 7;
}

SaveBitmapToFile((BYTE*)buf, 8, 8, 24, 0, "C:\\Users\\Chris\\Desktop\\Link_Sprite.bmp");

delete[] buf;
return = 0;

edit ... I thought the goal was to get rid of the if's
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Cyclone wrote:That didn't work when I tried that.

Ah, my fault. I must have glanced at the shifts which were looking fine and ignored everything else that was missing.

You did manage it with shifts which is one of the ways to do it. The other way would have been something like this:

Code: Select all
b = (!!(Bitplane0_ROW[p] & (1 << N)) << 1)
|  !!(Bitplane1_ROW[p] & (1 << N));

This way uses logical NOT twice. That has the effect of forcing the extracted set bits to 1 so you don't need to shift them right before shifting them left. It can sometimes make things a bit easier to think through.

As for the index values, you currently have four tables each containing three values. That's one table for each colour containing an R, G and B value. A more typical way would be to have three tables containing the R, the G and the B values. So that would be three tables containing four values. The idea is getting used to structuring data so it can be accessed using a single index.

Code: Select all
unsigned char   red[] = { 255,  53,  59, 154 };
unsigned char green[] = { 255, 189,  85, 194 };
unsigned char  blue[] = { 255, 104, 142, 237 };

...

buf[c + 0] =   red[index];
buf[c + 1] = green[index];
buf[c + 2] =  blue[index];

That has the effect of index 0 being White, index 1 being green, index 2 being Brown and index 3 being Tan, and with much less code involved.

It's often more convenient to put the red/green/blue data in a class or struct and access them that way:

Code: Select all
struct RGB {
unsigned char   red;
unsigned char green;
unsigned char  blue;
};

struct RGB colours[] = {
{ 255, 255, 255 }, // White
{  53, 189, 104 }, // Green
{  59,  85, 142 }, // Brown
{ 154, 194, 237 }  // Tan
};

...

buf[c + 0] = colours[index].red;
buf[c + 1] = colours[index].green;
buf[c + 2] = colours[index].blue;

Also be careful with your buffer, you've allocated 5*8 = 40 bytes, but the image data is 8*8*3 = 192 bytes. Even if it works without complaining, it's potentially very dangerous to write out of bounds like that. I might not be putting enough emphasis on it, but this is something you have to be really really really careful about.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:You did manage it with shifts which is one of the ways to do it. The other way would have been something like this:

Code: Select all
b = (!!(Bitplane0_ROW[p] & (1 << N)) << 1)
|  !!(Bitplane1_ROW[p] & (1 << N));

This way uses logical NOT twice. That has the effect of forcing the extracted set bits to 1 so you don't need to shift them right before shifting them left. It can sometimes make things a bit easier to think through.

Yes that works. But how do I combine 4 bits now? Since DKC Sprites are 4bpp I need to understand that. I've tried various things but I can get the right result.

And thanks for your help. I am learning lots.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

A 4-bit version is a very similar process. To construct the 4-bit index value you'd need to extract the four different bits and shift them into place as necessary. There isn't really anything new to cover at this stage, so maybe just try and think over how your 2-bit version works at each point and how it might be extended to work with 4-bits.

One thing to note is that 2-bits gives you values of 0-3, while 4-bits gives values 0-15. If your R/G/B tables have only 4 values, using a 4-bit index could cause it to read out of bounds. In other words it might be appropriate to extend the tables to contain 16 unique colours.

That's partially why I was leaning towards using shades of grey to start off with, as it would give you one less thing to think about right now. Usually a program would decode the colours into a table rather than them being hard-coded. That's probably the next adventure when we're done with bitplanes...
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:A 4-bit version is a very similar process. To construct the 4-bit index value you'd need to extract the four different bits and shift them into place as necessary. There isn't really anything new to cover at this stage, so maybe just try and think over how your 2-bit version works at each point and how it might be extended to work with 4-bits.

I dunno I am stumped. I spent like 8 hours on this. I always try my best before I ask questions. Sorry.
Thanks anyways.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Here... This works I think

Code: Select all
std::bitset<4> bs;
bs.set(0, (Bitplane0_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

std::bitset? You're well into C++ territory now. It should do the same thing, but I was personally hoping for one of these:

Code: Select all
int index =  ((Bitplane0_ROW[p] >> N) & 1)
| (((Bitplane1_ROW[p] >> N) & 1) << 1)
| (((Bitplane2_ROW[p] >> N) & 1) << 2)
| (((Bitplane3_ROW[p] >> N) & 1) << 3);

int index =  !!(Bitplane0_ROW[p] & (1 << N))
| (!!(Bitplane1_ROW[p] & (1 << N)) << 1)
| (!!(Bitplane2_ROW[p] & (1 << N)) << 2)
| (!!(Bitplane3_ROW[p] & (1 << N)) << 3);

The bp.set function you have there does more or less the same thing each time by setting the nth bit if the expression in the second argument is true. Bits are typically numbered with the lowest bit being bit 0, so the bits in an 8-bit byte would be numbered 7643210.

Some languages will have convenience functions for things like this, even if they're not actually more convenient. It doesn't hurt to understand what's going on with them, although we certainly shouldn't complain if it works!
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:As for the index values, you currently have four tables each containing three values. That's one table for each colour containing an R, G and B value. A more typical way would be to have three tables containing the R, the G and the B values. So that would be three tables containing four values. The idea is getting used to structuring data so it can be accessed using a single index.

Code: Select all
unsigned char   red[] = { 255,  53,  59, 154 };
unsigned char green[] = { 255, 189,  85, 194 };
unsigned char  blue[] = { 255, 104, 142, 237 };

...

buf[c + 0] =   red[index];
buf[c + 1] = green[index];
buf[c + 2] =  blue[index];

That has the effect of index 0 being White, index 1 being green, index 2 being Brown and index 3 being Tan, and with much less code involved.

It's often more convenient to put the red/green/blue data in a class or struct and access them that way:

Code: Select all
struct RGB {
unsigned char   red;
unsigned char green;
unsigned char  blue;
};

struct RGB colours[] = {
{ 255, 255, 255 }, // White
{  53, 189, 104 }, // Green
{  59,  85, 142 }, // Brown
{ 154, 194, 237 }  // Tan
};

...

buf[c + 0] = colours[index].red;
buf[c + 1] = colours[index].green;
buf[c + 2] = colours[index].blue;

I understand it but what is the best way of using ALL the 255 x 3 colours?
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Edit. Kingizor. There are a lot of errors in your post. you said. { 53, 189, 104 }, // Green
but that's actually blue...
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

I didn't notice that! I looked, that is really green!
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

^ I checked here... https://htmlcolorcodes.com/

you are correct its green. but I still get blue in my program...
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

What? It shows blue? That's confusing. I used paint to discover the true hue of this elusive color. Also, I am almost done with my proofreading of your manual.
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

I figured out what I'm doing wrong. I just don't know how to access the separate values in the Struct that's why.

Edit. I am getting a strange glitch in my code. I have

Code: Select all
unsigned char   red[] = { 255,  53,  59, 154, 0, 255 };
unsigned char green[] = { 255, 189,  85, 194, 255, 0};
unsigned char  blue[] = { 255, 104, 142, 237, 255, 0 };

and then this....

Code: Select all
else if (Index == 5) // red
{
buf[c + 0] = red[Index];
buf[c + 1] = green[Index];
buf[c + 2] = blue[Index];
}

I am getting blue and not red. the other colours seem to work.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Most graphics we'd encounter are made up of different types of RGB. A 24-bit BMP image would have a 8-bit (R)ed component, a 8-bit (G)reen component and a 8-bit (B)lue component.

For all of these 8-bit R/G/B components, their intensity has a range of 0-255, and different combinations of R/G/B will produce different colours. Some examples of the extremes would be:

255, 0, 0 // red
0, 255, 0 // green
0, 0, 255 // blue
0, 0, 0 // black
255, 255, 255 // white
255, 255, 0 // yellow
255, 0, 255 // magenta
0, 255, 255 // cyan

Images can also have transparency, which can either be represented a few ways. There are a few ways of handling transparency, but the simplest at this point would be an additional 8-bit (A)lpha component where a value of 0 would be fully transparent and 255 would be fully opaque.

Generally when we're dealing with bitplanes, we will have a table that consists of a certain number of different colours, each with a (R)ed, (G)reen and (B)lue component.

The index value therefore represents which colour we want to use.

In this post there are two sets of tables that have the data arranged slightly differently.

In this first version we have separate tables for the red, green and blue components. With our 2-bit index (0-3), we would be able to access the 0th, 1st, 2nd or 3rd element of each table.

If our index was 1, we'd get red[1], green[1] and blue[1] which would give us { 53, 189, 104 } which turns out to be a nice green shade. Similarly if our index was 2, we'd get { 59, 85, 142 } which is a deeper blue.

Spoiler!
When putting together those tables, I copied the colours directly from your own code as well as the commented colours.

53,189,104 is labelled as green and is a nice pale green.
59, 85,142 is labelled as brown, but it is actually a deep blue.
154,194,237 is labelled as tan, but it is a lighter blue.

In the second version we have a struct containing a red, a green and a blue component. After that we define an array containing four groups of colours. That code block can be scrolled, there is an example of how to access and assign them.

The idea behind using tables is that you don't need to check the value of the index in an if block at all, using e.g. red[index] will retrieve the nth shade of red for you automatically.

Both versions contain the same data, just arranged slightly differently. The second version is a bit more verbose, but also a bit easier to comprehend.

The extended table you have with 6 entries each are a bit concerning. We're dealing with bitplanes consisting of 2, 4 or 8 bits per plane, so each index will be 0-3, 0-15 or 0-255. If you were using 2bpp, only the first four entries in each table would be used (0-3). If you were using 4bpp, your index could be 0-15, so if it were higher than 5 it could read past the end of the table and the results could be unpredictable.

At this stage, getting a bit more familiar with different types of debugging would be a good idea. You should have a rough idea of what colours each pixel ought to be, so why not print out what colours are going where and ensure that it matches what you expect should be happening. If you're not getting what you expect, you can study the code and try to figure out why that happened at a particular point that would cause it to go wrong.

WesternTanager794 mentioned paint which is a very good idea. I don't know what capabilities it has nowadays, but most image editors or viewers will have an eyedropper tool or some equivalent that you can use to inspect individual pixels in your output image. It's also fairly common for them to use #RRGGBB notation for colours. Those are hexadecimal values so they should be familiar by now? Calculator programs can usually convert between decimal and hexadecimal if you need that.

Some image formats use BGR (or other formats) instead of RGB, but that shouldn't be an issue here.

Image manipulation is a terrific way to learn so many ins and outs of programming, so much of it is fundamental robotic logic.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Kingizor wrote:In the second version we have a struct containing a red, a green and a blue component. After that we define an array containing four groups of colours. That code block can be scrolled, there is an example of how to access and assign them.

The idea behind using tables is that you don't need to check the value of the index in an if block at all, using e.g. red[index] will retrieve the nth shade of red for you automatically.

Both versions contain the same data, just arranged slightly differently. The second version is a bit more verbose, but also a bit easier to comprehend.

I tried the struct but it wouldn't work for for me.

Thank you for the thorough explanations. I solved the issue I was having with the colours. BMP uses blue green red order. I was using red green and blue order...

I don't know how I didn't do this sooner but I got rid of all those if's! Tables are still probablyt the wrong way of doing things.

Code: Select all
int main()
{

int Bitplane0_ROW[] = { 0b01100110 , 0b11111111, 0b01011010, 0b01111110, 0b00000000, 0b10000001, 0b11111111, 0b01111110 }; // Array to to store numbers Last Row is first.
int Bitplane1_ROW[] = { 0b01111110, 0b11111111, 0b11111111, 0b11011011, 0b11111111, 0b01111110, 0b00000000, 0b00000000 };
int Bitplane2_ROW[] = { 0b00000000 , 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 };
int Bitplane3_ROW[] = { 0b00000000 , 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 };

// Colours 0 to 7
//int Bitplane0_ROW[] = { 0b11111111, 0b00000000,0b11111111,0b00000000,0b11111111,0b00000000,0b11111111,0b00000000 };
//int Bitplane1_ROW[] = { 0b11111111, 0b11111111,0b00000000,0b00000000,0b11111111,0b11111111,0b00000000,0b00000000 };
//int Bitplane2_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b00000000,0b00000000,0b00000000,0b00000000};
//int Bitplane3_ROW[] = { 0b00000000, 0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000 };

// Colours 8 -  15
//int Bitplane0_ROW[] = { 0b11111111, 0b00000000,0b11111111,0b00000000,0b11111111,0b00000000,0b11111111,0b00000000 };
//int Bitplane1_ROW[] = { 0b11111111, 0b11111111,0b00000000,0b00000000,0b11111111,0b11111111,0b00000000,0b00000000 };
//int Bitplane2_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b00000000,0b00000000,0b00000000,0b00000000 };
//int Bitplane3_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b11111111,0b11111111,0b11111111,0b11111111 };

int N = 7; //to store bit
int c = 0;

BYTE* buf = new BYTE[8 * 30];

unsigned char   red[] = { 255,  104, 142, 237, 255, 0,   255, 128,  255, 0, 0,   255, 128, 0,  0,   0, };
unsigned char green[] = { 255,  189, 85,  194, 255, 0,   0,   128,  0,   0, 255, 191, 0,   0,  255, 128 };
unsigned char  blue[] = { 255,  53,  59,  154, 0,   255, 0,   128,  255, 0, 255, 0,   0,  128, 0,   128 };

for (int p = 0; p < 8; p++)
{
for (int j = 0; j < 8; j++) // Row 6
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

cout << "The value  is " << Index;

buf[c + 0] = blue[Index];
buf[c + 1] = green[Index];
buf[c + 2] = red[Index];

c += 3;
N--;
}
N = 7;
}

SaveBitmapToFile((BYTE*)buf, 8, 8, 24, 0, "C:\\Users\\Chris\\Desktop\\Link_Sprite.bmp");

delete[] buf;

return 0;
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Here is the code I have so far.
How do I use all the thousands of colours the SNES can display? Do I use an array, create the different colours at run time? I'm not sure.

Code: Select all
int main()
{

int Bitplane0_ROW[] = { 0b01100110 , 0b11111111, 0b01011010, 0b01111110, 0b00000000, 0b10000001, 0b11111111, 0b01111110 }; // Array to to store numbers Last Row is first.
int Bitplane1_ROW[] = { 0b01111110, 0b11111111, 0b11111111, 0b11011011, 0b11111111, 0b01111110, 0b00000000, 0b00000000 };
int Bitplane2_ROW[] = { 0b00000000 , 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 };
int Bitplane3_ROW[] = { 0b00000000 , 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 };

// Colours 0 to 7
//int Bitplane0_ROW[] = { 0b11111111, 0b00000000,0b11111111,0b00000000,0b11111111,0b00000000,0b11111111,0b00000000 };
//int Bitplane1_ROW[] = { 0b11111111, 0b11111111,0b00000000,0b00000000,0b11111111,0b11111111,0b00000000,0b00000000 };
//int Bitplane2_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b00000000,0b00000000,0b00000000,0b00000000};
//int Bitplane3_ROW[] = { 0b00000000, 0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000 };

// Colours 8 -  15
//int Bitplane0_ROW[] = { 0b11111111, 0b00000000,0b11111111,0b00000000,0b11111111,0b00000000,0b11111111,0b00000000 };
//int Bitplane1_ROW[] = { 0b11111111, 0b11111111,0b00000000,0b00000000,0b11111111,0b11111111,0b00000000,0b00000000 };
//int Bitplane2_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b00000000,0b00000000,0b00000000,0b00000000 };
//int Bitplane3_ROW[] = { 0b11111111, 0b11111111,0b11111111,0b11111111,0b11111111,0b11111111,0b11111111,0b11111111 };

unsigned char colours[][3] =  // An array of colour values
{
//BBB  GGG  RRR
{ 255, 255, 255 }, /* White */
{  53, 189, 104 }, /* Green */
{  59,  85, 142 }, /* Brown */
{ 154, 194, 237 }, /* Tan */
{   0, 255, 255 }, /* Yellow */
{   0,   0, 255 }, /* Red */
{ 255,   0,   0 }, /* Blue */
{ 128, 128, 128 }, /* Grey */
{ 255,   0, 255 }, /* Purple */
{   0,   0,   0 }, /* Black */
{ 255, 255,   0 }, /* Aqua */
{   0, 191, 255 }, /* Orange */
{   0,   0, 128 }, /* Maroon */
{ 128,   0,   0 }, /* Navy */
{   0, 255,   0 }, /* Lime */
{ 128, 128,   0 }, /* Teal */
};

int c = 0;

BYTE* buf = new BYTE[8 * 96];

for (int p = 0; p < 8; p++)
{

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

buf[c++] = colours[Index][0];   // Blue
buf[c++] = colours[Index][1];   // Green
buf[c++] = colours[Index][2];   // Red

}

}

SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, "C:\\Users\\Chris\\Desktop\\Link_Sprite.bmp");

delete[] buf;

return 0;

}
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Cyclone wrote:How do I use all the thousands of colours the SNES can display? Do I use an array, create the different colours at run time? I'm not sure.

Personally, I just create at runtime. I'm not sure about the efficiency of using an array, but I imagine the difference is negligable.
Treasure Hunter
93
Posts: 495
Joined: 2016

### Re: Sprite Graphics

SNES uses 5-bits per component, so 5+5+5 = 15 bits giving a total of 32768 possible colours. The data is stored as 16-bit words and the layout is:

Code: Select all
FEDCBA98 76543210
-BBBBBGG GGGRRRRR

SNES is little-endian, so the low byte comes before the high byte. Be careful not to read the bytes in the wrong order.

For each sprite object there is matching palette data. All sprite tiles are 4bpp, so they always use (up to) 16 colours. You would typically load the relevant palette data from ROM and convert it from the above BGR555 format to the more familiar RGB888. Some of the earlier posts discuss a few ways of doing this.

If a component is 5-bits, it has a range of 0-31, with 0 being the least intense and 31 being the most intense. Similarly, if a component is 8-bits, it has a range of 0-255, with 0 being the least intense and 255 being the most intense. The question is how can we convert from one to the other?

It doesn't have to be a perfect conversion, and you don't have to overthink it. It's probably better to think about it in terms of plain numbers first before writing any code.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

This is how I convert at runtime.
Code: Select all
public static Color ConvertSNEStoColor(int value)
{
int r = (value >> 0) & 0x1f;
int g = (value >> 5) & 0x1f;
int b = (value >> 10) & 0x1f;

// SNES rgb is 5 bits each, shift 3 for 8 each for standard rgb
Color @return = Color.FromArgb((r << 3), (g << 3), (b << 3));
return @return;
}
Treasure Hunter
93
Posts: 495
Joined: 2016

### Re: Sprite Graphics

So I almost got it working perfectly but there is one error in it. Whenever it encounters a value of 0x1A in the tile code it does not write the bitmap perfectly.
Any Ideas? Thanks.

The below code will extract the kong letters
Code: Select all
int main()
{

CString File_Path;
char sztmp[1024];

cin.clear();
cout << "Enter Path to extract sprite graphics to: " << endl;
cin.getline(sztmp, sizeof(sztmp), '\n');
File_Path = sztmp;

unsigned char colours[][3] =  // An array of colour values
{
//BBB  GGG  RRR
{ 255, 255, 255 }, /* White */
{  0, 48, 64 }, /* Index 1 */
{  0,  72, 112 }, /* Index 2 */
{ 56, 104, 168 }, /* Index 3 */
{   0, 152, 216 }, /* Index 4*/
{   8, 216, 248 }, /*  Index 5 */
{ 255,   255,  255 }, /* Index 6 */
{ 208, 208, 208 }, /* Index 7 */
{ 160, 160, 160 }, /* Index 8 */
{   0,   0,   0 }, /* Black */
{ 0, 0,  72 }, //  Index 10
{   0, 191, 255 }, /* Orange */
{   0,   0, 128 }, /* Maroon */
{ 128,   0,   0 }, /* Navy */
{   0, 255,   0 }, /* Lime */
{ 128, 128,   0 }, /* Teal */
};

char oData[6000]; // The Array
ifstream inFile;
inFile.seekg(0x347519 + 10, 1);
inFile.close();

int E = 0;

for (int X = 0; X <= 30; X++)

{

// Letter K -- Top Left Tile
int* Bitplane0_Top_Left_ROW = new int[8];
int* Bitplane1_Top_Left_ROW = new int[8];
int* Bitplane2_Top_Left_ROW = new int[8];
int* Bitplane3_Top_Left_ROW = new int[8];

Bitplane0_Top_Left_ROW[0] = oData[14 + E];
Bitplane0_Top_Left_ROW[1] = oData[12 + E];
Bitplane0_Top_Left_ROW[2] = oData[10 + E];
Bitplane0_Top_Left_ROW[3] = oData[8 + E];
Bitplane0_Top_Left_ROW[4] = oData[6 + E];
Bitplane0_Top_Left_ROW[5] = oData[4 + E];
Bitplane0_Top_Left_ROW[6] = oData[2 + E];
Bitplane0_Top_Left_ROW[7] = oData[0 + E];

Bitplane1_Top_Left_ROW[0] = oData[15 + E];
Bitplane1_Top_Left_ROW[1] = oData[13 + E];
Bitplane1_Top_Left_ROW[2] = oData[11 + E];
Bitplane1_Top_Left_ROW[3] = oData[9 + E];
Bitplane1_Top_Left_ROW[4] = oData[7 + E];
Bitplane1_Top_Left_ROW[5] = oData[5 + E];
Bitplane1_Top_Left_ROW[6] = oData[3 + E];
Bitplane1_Top_Left_ROW[7] = oData[1 + E];

Bitplane2_Top_Left_ROW[0] = oData[30 + E];
Bitplane2_Top_Left_ROW[1] = oData[28 + E];
Bitplane2_Top_Left_ROW[2] = oData[26 + E];
Bitplane2_Top_Left_ROW[3] = oData[24 + E];
Bitplane2_Top_Left_ROW[4] = oData[22 + E];
Bitplane2_Top_Left_ROW[5] = oData[20 + E];
Bitplane2_Top_Left_ROW[6] = oData[18 + E];
Bitplane2_Top_Left_ROW[7] = oData[16 + E];

Bitplane3_Top_Left_ROW[0] = oData[31 + E];
Bitplane3_Top_Left_ROW[1] = oData[29 + E];
Bitplane3_Top_Left_ROW[2] = oData[27 + E];
Bitplane3_Top_Left_ROW[3] = oData[25 + E];
Bitplane3_Top_Left_ROW[4] = oData[23 + E];
Bitplane3_Top_Left_ROW[5] = oData[21 + E];
Bitplane3_Top_Left_ROW[6] = oData[19 + E];
Bitplane3_Top_Left_ROW[7] = oData[17 + E];

// Letter K -- Top Right Tile
int* Bitplane0_Top_Right_ROW = new int[8];
int* Bitplane1_Top_Right_ROW = new int[8];
int* Bitplane2_Top_Right_ROW = new int[8];
int* Bitplane3_Top_Right_ROW = new int[8];

Bitplane0_Top_Right_ROW[0] = oData[46 + E];
Bitplane0_Top_Right_ROW[1] = oData[44 + E];
Bitplane0_Top_Right_ROW[2] = oData[42 + E];
Bitplane0_Top_Right_ROW[3] = oData[40 + E];
Bitplane0_Top_Right_ROW[4] = oData[38 + E];
Bitplane0_Top_Right_ROW[5] = oData[36 + E];
Bitplane0_Top_Right_ROW[6] = oData[34 + E];
Bitplane0_Top_Right_ROW[7] = oData[32 + E];

Bitplane1_Top_Right_ROW[0] = oData[47 + E];
Bitplane1_Top_Right_ROW[1] = oData[45 + E];
Bitplane1_Top_Right_ROW[2] = oData[43 + E];
Bitplane1_Top_Right_ROW[3] = oData[41 + E];
Bitplane1_Top_Right_ROW[4] = oData[39 + E];
Bitplane1_Top_Right_ROW[5] = oData[37 + E];
Bitplane1_Top_Right_ROW[6] = oData[35 + E];
Bitplane1_Top_Right_ROW[7] = oData[33 + E];

Bitplane2_Top_Right_ROW[0] = oData[62 + E];
Bitplane2_Top_Right_ROW[1] = oData[60 + E];
Bitplane2_Top_Right_ROW[2] = oData[58 + E];
Bitplane2_Top_Right_ROW[3] = oData[56 + E];
Bitplane2_Top_Right_ROW[4] = oData[54 + E];
Bitplane2_Top_Right_ROW[5] = oData[52 + E];
Bitplane2_Top_Right_ROW[6] = oData[50 + E];
Bitplane2_Top_Right_ROW[7] = oData[48 + E];

Bitplane3_Top_Right_ROW[0] = oData[63 + E];
Bitplane3_Top_Right_ROW[1] = oData[61 + E];
Bitplane3_Top_Right_ROW[2] = oData[59 + E];
Bitplane3_Top_Right_ROW[3] = oData[57 + E];
Bitplane3_Top_Right_ROW[4] = oData[55 + E];
Bitplane3_Top_Right_ROW[5] = oData[53 + E];
Bitplane3_Top_Right_ROW[6] = oData[51 + E];
Bitplane3_Top_Right_ROW[7] = oData[49 + E];

// Letter K -- Bottom Left Tile
int* Bitplane0_Bottom_Left_ROW = new int[8];
int* Bitplane1_Bottom_Left_ROW = new int[8];
int* Bitplane2_Bottom_Left_ROW = new int[8];
int* Bitplane3_Bottom_Left_ROW = new int[8];

Bitplane0_Bottom_Left_ROW[0] = oData[78 + E];
Bitplane0_Bottom_Left_ROW[1] = oData[76 + E];
Bitplane0_Bottom_Left_ROW[2] = oData[74 + E];
Bitplane0_Bottom_Left_ROW[3] = oData[72 + E];
Bitplane0_Bottom_Left_ROW[4] = oData[70 + E];
Bitplane0_Bottom_Left_ROW[5] = oData[68 + E];
Bitplane0_Bottom_Left_ROW[6] = oData[66 + E];
Bitplane0_Bottom_Left_ROW[7] = oData[64 + E];

Bitplane1_Bottom_Left_ROW[0] = oData[79 + E];
Bitplane1_Bottom_Left_ROW[1] = oData[77 + E];
Bitplane1_Bottom_Left_ROW[2] = oData[75 + E];
Bitplane1_Bottom_Left_ROW[3] = oData[73 + E];
Bitplane1_Bottom_Left_ROW[4] = oData[71 + E];
Bitplane1_Bottom_Left_ROW[5] = oData[69 + E];
Bitplane1_Bottom_Left_ROW[6] = oData[67 + E];
Bitplane1_Bottom_Left_ROW[7] = oData[65 + E];

Bitplane2_Bottom_Left_ROW[0] = oData[94 + E];
Bitplane2_Bottom_Left_ROW[1] = oData[92 + E];
Bitplane2_Bottom_Left_ROW[2] = oData[90 + E];
Bitplane2_Bottom_Left_ROW[3] = oData[88 + E];
Bitplane2_Bottom_Left_ROW[4] = oData[86 + E];
Bitplane2_Bottom_Left_ROW[5] = oData[84 + E];
Bitplane2_Bottom_Left_ROW[6] = oData[82 + E];
Bitplane2_Bottom_Left_ROW[7] = oData[80 + E];

Bitplane3_Bottom_Left_ROW[0] = oData[95 + E];
Bitplane3_Bottom_Left_ROW[1] = oData[93 + E];
Bitplane3_Bottom_Left_ROW[2] = oData[91 + E];
Bitplane3_Bottom_Left_ROW[3] = oData[89 + E];
Bitplane3_Bottom_Left_ROW[4] = oData[87 + E];
Bitplane3_Bottom_Left_ROW[5] = oData[85 + E];
Bitplane3_Bottom_Left_ROW[6] = oData[83 + E];
Bitplane3_Bottom_Left_ROW[7] = oData[81 + E];

// Letter K -- Bottom Right Tile
int* Bitplane0_Bottom_Right_ROW = new int[8];
int* Bitplane1_Bottom_Right_ROW = new int[8];
int* Bitplane2_Bottom_Right_ROW = new int[8];
int* Bitplane3_Bottom_Right_ROW = new int[8];

Bitplane0_Bottom_Right_ROW[0] = oData[110 + E];
Bitplane0_Bottom_Right_ROW[1] = oData[108 + E];
Bitplane0_Bottom_Right_ROW[2] = oData[106 + E];
Bitplane0_Bottom_Right_ROW[3] = oData[104 + E];
Bitplane0_Bottom_Right_ROW[4] = oData[102 + E];
Bitplane0_Bottom_Right_ROW[5] = oData[100 + E];
Bitplane0_Bottom_Right_ROW[6] = oData[98 + E];
Bitplane0_Bottom_Right_ROW[7] = oData[96 + E];

Bitplane1_Bottom_Right_ROW[0] = oData[111 + E];
Bitplane1_Bottom_Right_ROW[1] = oData[109 + E];
Bitplane1_Bottom_Right_ROW[2] = oData[107 + E];
Bitplane1_Bottom_Right_ROW[3] = oData[105 + E];
Bitplane1_Bottom_Right_ROW[4] = oData[103 + E];
Bitplane1_Bottom_Right_ROW[5] = oData[101 + E];
Bitplane1_Bottom_Right_ROW[6] = oData[99 + E];
Bitplane1_Bottom_Right_ROW[7] = oData[97 + E];

Bitplane2_Bottom_Right_ROW[0] = oData[126 + E];
Bitplane2_Bottom_Right_ROW[1] = oData[124 + E];
Bitplane2_Bottom_Right_ROW[2] = oData[122 + E];
Bitplane2_Bottom_Right_ROW[3] = oData[120 + E];
Bitplane2_Bottom_Right_ROW[4] = oData[118 + E];
Bitplane2_Bottom_Right_ROW[5] = oData[116 + E];
Bitplane2_Bottom_Right_ROW[6] = oData[114 + E];
Bitplane2_Bottom_Right_ROW[7] = oData[112 + E];

Bitplane3_Bottom_Right_ROW[0] = oData[127 + E];
Bitplane3_Bottom_Right_ROW[1] = oData[125 + E];
Bitplane3_Bottom_Right_ROW[2] = oData[123 + E];
Bitplane3_Bottom_Right_ROW[3] = oData[121 + E];
Bitplane3_Bottom_Right_ROW[4] = oData[119 + E];
Bitplane3_Bottom_Right_ROW[5] = oData[117 + E];
Bitplane3_Bottom_Right_ROW[6] = oData[115 + E];
Bitplane3_Bottom_Right_ROW[7] = oData[113 + E];

E = E + 128 + 10;

int c = 0;

BYTE* buf = new BYTE[3264];

for (int p = 0; p < 8; p++) // Bottom Left Tile
{

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

buf[c++] = colours[Index][0];   // Blue
buf[c++] = colours[Index][1];   // Green
buf[c++] = colours[Index][2];   // Red
}

if (p == 0) { c = 48; }
if (p == 1) { c = 96; }
if (p == 2) { c = 144; }
if (p == 3) { c = 192; }
if (p == 4) { c = 240; }
if (p == 5) { c = 288; }
if (p == 6) { c = 336; }
}

for (int p = 0; p < 8; p++) // Top Left Tile
{
if (p == 0) { c = 384; }
if (p == 1) { c = 432; }
if (p == 2) { c = 480; }
if (p == 3) { c = 528; }
if (p == 4) { c = 576; }
if (p == 5) { c = 624; }
if (p == 6) { c = 672; }
if (p == 7) { c = 720; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

buf[c++] = colours[Index][0];   // Blue
buf[c++] = colours[Index][1];   // Green
buf[c++] = colours[Index][2];   // Red

}
}
for (int p = 0; p < 8; p++) // Bottom Right Tile.
{
if (p == 0) { c = 24; }
if (p == 1) { c = 72; }
if (p == 2) { c = 120; }
if (p == 3) { c = 168; }
if (p == 4) { c = 216; }
if (p == 5) { c = 264; }
if (p == 6) { c = 312; }
if (p == 7) { c = 360; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Right_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

buf[c++] = colours[Index][0];   // Blue
buf[c++] = colours[Index][1];   // Green
buf[c++] = colours[Index][2];   // Red

}

}
for (int p = 0; p < 8; p++) // Top Right Tile.
{
if (p == 0) { c = 408; }
if (p == 1) { c = 456; }
if (p == 2) { c = 504; }
if (p == 3) { c = 552; }
if (p == 4) { c = 600; }
if (p == 5) { c = 648; }
if (p == 6) { c = 696; }
if (p == 7) { c = 744; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Right_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

buf[c++] = colours[Index][0];   // Blue
buf[c++] = colours[Index][1];   // Green
buf[c++] = colours[Index][2];   // Red

}

}

if (X == 0)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 1)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 2)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 3)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 4)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 5)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 6)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 7)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 8)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 9)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 10)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 11)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 12)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 13)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 14)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 15)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 16)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 17)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 18)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 19)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 20)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 21)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 22)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 23)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 24)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_Z.bmp");
}

if (X == 25)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 26)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 27)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 28)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 29)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

if (X == 30)
{
SaveBitmapToFile((BYTE*)buf, 16, 16, 24, 0, File_Path + X.ToString() + "_Letter_K.bmp");
}

delete[] buf;

}

return 0;

}

553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

I’ll take a look! Glad you’re back, Cyclone!
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

I will try to explain my issue with some images.

Notice the pixels don't match.
1pxoffb.png (21.68 KiB) Viewed 3893 times

The below is the default value of 1A which causes the image that is created to not show up correctly.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Also, I just remembered two things! I never finished proofreading your document and I did not ever find the 3D model of our local 200 year old barn.
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

Any one?

Here is the source code. I'm using Microsoft Visual Studio 2019

https://www.mediafire.com/file/4hg67n9m ... B.zip/file

Is there some sort of pixel that is Set/not or something?

It just doesn't make sense that a value of 0x1a would cause such an error.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

I've spent a a while going over it and I can't find anything very obvious. I wrote a simple bitmap writer function that doesn't require Windows and it seems to produce the correct output.

With C and C++, if something unexplainable is happening at a certain point, it's quite often due to some kind of undefined behaviour occurring at an earlier point. Writing out of bounds one of the most common causes, but there doesn't seem to be anything like that happening.

This line is a bit dangerous:
Code: Select all
inFile.seekg(0x347519 + 10, 1);

There are two forms for ifstream.seekg. If you provide a single argument, that gets treated as the absolute address. If you provide a second argument, the first arg gets treated as a relative offset while the second describes relative from what; start of the file, current position or end of the file. These are called using platform described constants, e.g. std::ios::cur. In other words a 1 could mean anything there, it might even mean something different in MSVC which you're using compared to GCC that I'm using. So just:

Code: Select all
inFile.seekg(0x347519 + 10);

ought to be enough. That shouldn't be the issue here though, if it were causing problems you probably wouldn't be getting legible output at all.

You're using new for all those bitplane allocations (i.e. Bitplane0_Top_Left_ROW), but not calling delete. These end up being memory leaks! In classic C++, you're supposed to have an equivalent delete for every new. In modern C++ I believe there are other methods, but I'm not too caught up on the details (unique_ptr?).

Try to get into the habit of spotting duplicate code, or very code that seems very similar to other code. If you can do that, you can often accomplish the same things with much less and make it much simpler at the same time. You have 40 if blocks with 40 identical calls to the bitmap function, when that could be done with just a few lines of code. Most of the code involving the Bitplane variables could be reduced as well.

One other thing that I'd suggest is verifying that your ROM is correct. I think you have ZSNES and/or Snes9x nearby, so if you load it in one of them and check the message they display immediately after loading. If they say "Bad ROM" or "Incorrect Checksum", then that would suggest your ROM has been modified at some point which could be the source of the problem with the bad 'A'. Alternatively you could checksum the ROM yourself and compare to a known good hash. The CRC32 of DKC USA is C946DCA0. Some hex editors may have that functionality.

I've made an attempt at tidying it up a bit. I started doing it to get it to compile, but I've ended up removing a lot of the duplicate code. I've added a few error checks when dealing with files, sometimes things can go wrong and it can be helpful to know why. There are still a number of opportunities at tidying the code up.

C++ is a language that complains a lot, I'm not used to that. Don't put too much faith into any of the changes I've made. If anything looks wrong then there is a good chance it could be!

Also, you didn't have to zip your whole project folder, just the source file (main.cpp) would have been enough!
Attachments
main.zip
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Thanks for looking at my code. Did you get it to work without the pixel error? Again this happens with other sprites as well. I mean when there is a HEX value of 1a in the tile row code. When this happens it messes up the tiles after it too. To prevent it messing up other tiles I have to change the 0x1a value to something else but the problem tile remains 1px/row off. How weird!

checksum.jpg (91.93 KiB) Viewed 3665 times
Attachments
main.zip
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

On the top are the letters generated by the program, montaged together, and on the bottom is the same letter configuration produced by the text generator thing we have here.

generated by main.cpp
letters.png (1.28 KiB) Viewed 3650 times

I actually wasn't expecting it, but they do seem to be identical.

There could still be a bug in the program that we've overlooked that's causing it to run a bit differently for you, but I'm not convinced that's the case.

Your ROM seems fine, providing that's definitely the same one being used. The CRC32 at the bottom right of the image matches the one I posted, and it reports that the internal checksum is fine too.

There could still be other kinds of errors going on. When the letter images get created by the program, the last modified date should be updated for them too. If that weren't the case, that would suggest that you'd be looking at an old copy.

I also noticed in the project folder you posted that you have both release and debug configurations there. I haven't used Visual Studio for a long time, but I believe it only recompiles one of these depending on whether you're in release mode or debug mode. If you were running an old version of one of them, any changes you made wouldn't show up. That probably shouldn't happen if you're running them directly from the Visual Studio, or at least I hope it wouldn't. Either way, you know where your project folder is so checking the last modified date of the compiled executable and running it directly would rule out that being a problem.

If it's none of those things, then I'm really not sure what the problem is. Some careful debugging is usually in order in situations like these. You could insert a cout or two to print the colour data as it is getting written to the relevant position in the buffer, and compare that to the what's in the output file with your hex editor. That might give you an idea of where things are going wrong, and whether there is a problem happening earlier or later.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

I didn’t actually put it through a thorough analysis. I’m sorry I didn’t put a lot of time into it. But from my preliminary analysis, I didn’t find anything immediately wrong. Also, it looks like you altered the Rareware logo! I like it!
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

OK. SO I gave up on writing to the bmp format...
I am now trying to get the same code working with a png.
I am having difficulties in getting it to work. I went back a version and I know there's lots of errors in the code but I understood it better.

Help appreciated. Thanks
Attachments
PNG.zip
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

When I next get on a computer, I’ll take a look! This could be anywhere from two days to a week, though. When every mystery concerning sprite graphics is solved though, I think we will all understand it much better than before! This is really good work, Cyclone and Kingizor!
81
Posts: 1000
Joined: 2022

### Re: Sprite Graphics

This is as close as I got so far. It kinda works but its rotated and mirrored and is missing the last row of pixels.. Not sure why.
test.png (230 Bytes) Viewed 3470 times

Spoiler!
/*
LodePNG Examples

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.

3. This notice may not be removed or altered from any source
distribution.
*/

#include "C:\Users\Chris\source\repos\PNG\lodepng.h"
#include "C:\Users\Chris\source\repos\PNG\lodepng.cpp"
#include <Windows.h>
#include <algorithm>
#include <memory>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <atlstr.h>
#include <msclr/marshal_cppstd.h>

#include <iterator>

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include <stdio.h>

#include <iostream>
#include <bitset>
using namespace std;

//Encode from raw pixels to disk with a single function call
//The image argument has width * height RGBA pixels or width * height * 4 bytes
void encodeOneStep(const char* filename, std::vector<unsigned char>& image, unsigned width, unsigned height) {
//Encode the image
unsigned error = lodepng::encode(filename, image, width, height);

//if there's an error, display it
if (error) std::cout << "encoder error " << error << ": " << lodepng_error_text(error) << std::endl;
}

int main(int argc, char* argv[])
{
const unsigned width = 16, height = 16, no_elems = 4088;
std::vector<unsigned char> image(no_elems);
const char* filename = argc > 1 ? argv[1] : "test.png";

// Pallette
static const unsigned char colours[16][4] = { // An array of colour values
//BBB GGG RRR AA
{ 255, 255, 255,255 }, /* White */
{ 64, 48, 0, 255 }, /* Index 1 */ // reversed
{ 112, 72, 0, 255 }, /* Index 2 */ // reversed
{ 168, 104, 56, 255 }, /* Index 3 */ // reversed
{ 216, 152, 0, 255 }, /* Index 4 */ // reversed
{ 248, 216, 8, 255 }, /* Index 5 */ // reversed
{ 255, 255, 255, 255 }, /* Index 6 */ // reversed
{ 208, 208, 208, 255 }, /* Index 7 */
{ 160, 160, 160, 255 }, /* Index 8 */
{ 112, 120, 120, 255 }, /* Index 9 */
{ 72, 0, 0, 255 }, /* Index 10 */ // reversed
{ 160, 8, 8, 255 }, /* Index 11 */ // reversed
{ 248, 16, 16, 255 }, /* Index 12 */ // reversed
{ 248, 104, 8, 255 }, /* Index 13 */ // reversed
{ 0, 248, 0, 255 }, /* Index 14 */
{ 0, 0, 0, 255 } /* Index 15 */
};

char oData[0x6000]; // The Array Letter K
ifstream inFile;
inFile.seekg(0x347A7D + 10, 1);
inFile.close();

int E = 0;
int F = 0;

int* Bitplane0_Top_Left_ROW = new int[8];
int* Bitplane1_Top_Left_ROW = new int[8];
int* Bitplane2_Top_Left_ROW = new int[8];
int* Bitplane3_Top_Left_ROW = new int[8];

int* Bitplane0_Top_Right_ROW = new int[8];
int* Bitplane1_Top_Right_ROW = new int[8];
int* Bitplane2_Top_Right_ROW = new int[8];
int* Bitplane3_Top_Right_ROW = new int[8];

int* Bitplane0_Bottom_Left_ROW = new int[8];
int* Bitplane1_Bottom_Left_ROW = new int[8];
int* Bitplane2_Bottom_Left_ROW = new int[8];
int* Bitplane3_Bottom_Left_ROW = new int[8];

int* Bitplane0_Bottom_Right_ROW = new int[8];
int* Bitplane1_Bottom_Right_ROW = new int[8];
int* Bitplane2_Bottom_Right_ROW = new int[8];
int* Bitplane3_Bottom_Right_ROW = new int[8];

for (int X = 0; X <= 40; X++)

{

// Letter K -- Top Left Tile
Bitplane0_Top_Left_ROW[0] = oData[14 + E];
Bitplane0_Top_Left_ROW[1] = oData[12 + E];
Bitplane0_Top_Left_ROW[2] = oData[10 + E];
Bitplane0_Top_Left_ROW[3] = oData[8 + E];
Bitplane0_Top_Left_ROW[4] = oData[6 + E];
Bitplane0_Top_Left_ROW[5] = oData[4 + E];
Bitplane0_Top_Left_ROW[6] = oData[2 + E];
Bitplane0_Top_Left_ROW[7] = oData[0 + E];

Bitplane1_Top_Left_ROW[0] = oData[15 + E];
Bitplane1_Top_Left_ROW[1] = oData[13 + E];
Bitplane1_Top_Left_ROW[2] = oData[11 + E];
Bitplane1_Top_Left_ROW[3] = oData[9 + E];
Bitplane1_Top_Left_ROW[4] = oData[7 + E];
Bitplane1_Top_Left_ROW[5] = oData[5 + E];
Bitplane1_Top_Left_ROW[6] = oData[3 + E];
Bitplane1_Top_Left_ROW[7] = oData[1 + E];

Bitplane2_Top_Left_ROW[0] = oData[30 + E];
Bitplane2_Top_Left_ROW[1] = oData[28 + E];
Bitplane2_Top_Left_ROW[2] = oData[26 + E];
Bitplane2_Top_Left_ROW[3] = oData[24 + E];
Bitplane2_Top_Left_ROW[4] = oData[22 + E];
Bitplane2_Top_Left_ROW[5] = oData[20 + E];
Bitplane2_Top_Left_ROW[6] = oData[18 + E];
Bitplane2_Top_Left_ROW[7] = oData[16 + E];

Bitplane3_Top_Left_ROW[0] = oData[31 + E];
Bitplane3_Top_Left_ROW[1] = oData[29 + E];
Bitplane3_Top_Left_ROW[2] = oData[27 + E];
Bitplane3_Top_Left_ROW[3] = oData[25 + E];
Bitplane3_Top_Left_ROW[4] = oData[23 + E];
Bitplane3_Top_Left_ROW[5] = oData[21 + E];
Bitplane3_Top_Left_ROW[6] = oData[19 + E];
Bitplane3_Top_Left_ROW[7] = oData[17 + E];

// Letter K -- Top Right Tile

Bitplane0_Top_Right_ROW[0] = oData[46 + E];
Bitplane0_Top_Right_ROW[1] = oData[44 + E];
Bitplane0_Top_Right_ROW[2] = oData[42 + E];
Bitplane0_Top_Right_ROW[3] = oData[40 + E];
Bitplane0_Top_Right_ROW[4] = oData[38 + E];
Bitplane0_Top_Right_ROW[5] = oData[36 + E];
Bitplane0_Top_Right_ROW[6] = oData[34 + E];
Bitplane0_Top_Right_ROW[7] = oData[32 + E];

Bitplane1_Top_Right_ROW[0] = oData[47 + E];
Bitplane1_Top_Right_ROW[1] = oData[45 + E];
Bitplane1_Top_Right_ROW[2] = oData[43 + E];
Bitplane1_Top_Right_ROW[3] = oData[41 + E];
Bitplane1_Top_Right_ROW[4] = oData[39 + E];
Bitplane1_Top_Right_ROW[5] = oData[37 + E];
Bitplane1_Top_Right_ROW[6] = oData[35 + E];
Bitplane1_Top_Right_ROW[7] = oData[33 + E];

Bitplane2_Top_Right_ROW[0] = oData[62 + E];
Bitplane2_Top_Right_ROW[1] = oData[60 + E];
Bitplane2_Top_Right_ROW[2] = oData[58 + E];
Bitplane2_Top_Right_ROW[3] = oData[56 + E];
Bitplane2_Top_Right_ROW[4] = oData[54 + E];
Bitplane2_Top_Right_ROW[5] = oData[52 + E];
Bitplane2_Top_Right_ROW[6] = oData[50 + E];
Bitplane2_Top_Right_ROW[7] = oData[48 + E];

Bitplane3_Top_Right_ROW[0] = oData[63 + E];
Bitplane3_Top_Right_ROW[1] = oData[61 + E];
Bitplane3_Top_Right_ROW[2] = oData[59 + E];
Bitplane3_Top_Right_ROW[3] = oData[57 + E];
Bitplane3_Top_Right_ROW[4] = oData[55 + E];
Bitplane3_Top_Right_ROW[5] = oData[53 + E];
Bitplane3_Top_Right_ROW[6] = oData[51 + E];
Bitplane3_Top_Right_ROW[7] = oData[49 + E];

// Letter K -- Bottom Left Tile

Bitplane0_Bottom_Left_ROW[0] = oData[78 + E];
Bitplane0_Bottom_Left_ROW[1] = oData[76 + E];
Bitplane0_Bottom_Left_ROW[2] = oData[74 + E];
Bitplane0_Bottom_Left_ROW[3] = oData[72 + E];
Bitplane0_Bottom_Left_ROW[4] = oData[70 + E];
Bitplane0_Bottom_Left_ROW[5] = oData[68 + E];
Bitplane0_Bottom_Left_ROW[6] = oData[66 + E];
Bitplane0_Bottom_Left_ROW[7] = oData[64 + E];

Bitplane1_Bottom_Left_ROW[0] = oData[79 + E];
Bitplane1_Bottom_Left_ROW[1] = oData[77 + E];
Bitplane1_Bottom_Left_ROW[2] = oData[75 + E];
Bitplane1_Bottom_Left_ROW[3] = oData[73 + E];
Bitplane1_Bottom_Left_ROW[4] = oData[71 + E];
Bitplane1_Bottom_Left_ROW[5] = oData[69 + E];
Bitplane1_Bottom_Left_ROW[6] = oData[67 + E];
Bitplane1_Bottom_Left_ROW[7] = oData[65 + E];

Bitplane2_Bottom_Left_ROW[0] = oData[94 + E];
Bitplane2_Bottom_Left_ROW[1] = oData[92 + E];
Bitplane2_Bottom_Left_ROW[2] = oData[90 + E];
Bitplane2_Bottom_Left_ROW[3] = oData[88 + E];
Bitplane2_Bottom_Left_ROW[4] = oData[86 + E];
Bitplane2_Bottom_Left_ROW[5] = oData[84 + E];
Bitplane2_Bottom_Left_ROW[6] = oData[82 + E];
Bitplane2_Bottom_Left_ROW[7] = oData[80 + E];

Bitplane3_Bottom_Left_ROW[0] = oData[95 + E];
Bitplane3_Bottom_Left_ROW[1] = oData[93 + E];
Bitplane3_Bottom_Left_ROW[2] = oData[91 + E];
Bitplane3_Bottom_Left_ROW[3] = oData[89 + E];
Bitplane3_Bottom_Left_ROW[4] = oData[87 + E];
Bitplane3_Bottom_Left_ROW[5] = oData[85 + E];
Bitplane3_Bottom_Left_ROW[6] = oData[83 + E];
Bitplane3_Bottom_Left_ROW[7] = oData[81 + E];

// Letter K -- Bottom Right Tile

Bitplane0_Bottom_Right_ROW[0] = oData[110 + E];
Bitplane0_Bottom_Right_ROW[1] = oData[108 + E];
Bitplane0_Bottom_Right_ROW[2] = oData[106 + E];
Bitplane0_Bottom_Right_ROW[3] = oData[104 + E];
Bitplane0_Bottom_Right_ROW[4] = oData[102 + E];
Bitplane0_Bottom_Right_ROW[5] = oData[100 + E];
Bitplane0_Bottom_Right_ROW[6] = oData[98 + E];
Bitplane0_Bottom_Right_ROW[7] = oData[96 + E];

Bitplane1_Bottom_Right_ROW[0] = oData[111 + E];
Bitplane1_Bottom_Right_ROW[1] = oData[109 + E];
Bitplane1_Bottom_Right_ROW[2] = oData[107 + E];
Bitplane1_Bottom_Right_ROW[3] = oData[105 + E];
Bitplane1_Bottom_Right_ROW[4] = oData[103 + E];
Bitplane1_Bottom_Right_ROW[5] = oData[101 + E];
Bitplane1_Bottom_Right_ROW[6] = oData[99 + E];
Bitplane1_Bottom_Right_ROW[7] = oData[97 + E];

Bitplane2_Bottom_Right_ROW[0] = oData[126 + E];
Bitplane2_Bottom_Right_ROW[1] = oData[124 + E];
Bitplane2_Bottom_Right_ROW[2] = oData[122 + E];
Bitplane2_Bottom_Right_ROW[3] = oData[120 + E];
Bitplane2_Bottom_Right_ROW[4] = oData[118 + E];
Bitplane2_Bottom_Right_ROW[5] = oData[116 + E];
Bitplane2_Bottom_Right_ROW[6] = oData[114 + E];
Bitplane2_Bottom_Right_ROW[7] = oData[112 + E];

Bitplane3_Bottom_Right_ROW[0] = oData[127 + E];
Bitplane3_Bottom_Right_ROW[1] = oData[125 + E];
Bitplane3_Bottom_Right_ROW[2] = oData[123 + E];
Bitplane3_Bottom_Right_ROW[3] = oData[121 + E];
Bitplane3_Bottom_Right_ROW[4] = oData[119 + E];
Bitplane3_Bottom_Right_ROW[5] = oData[117 + E];
Bitplane3_Bottom_Right_ROW[6] = oData[115 + E];
Bitplane3_Bottom_Right_ROW[7] = oData[113 + E];

E = E + 128 + 10;

int c = 0;
BYTE* buf = new BYTE[3264];

for (int p = 0; p < 8; p++) // Top Left Tile
{

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}

if (p == 0) { c = 64; }
if (p == 1) { c = 128; }
if (p == 2) { c = 192; }
if (p == 3) { c = 256; }
if (p == 4) { c = 320; }
if (p == 5) { c = 384; }
if (p == 6) { c = 448; }
if (p == 7) { c = 512; }

}

for (int p = 0; p < 8; p++) // Bottom Left Tile
{

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}

if (p == 0) { c = 576; }
if (p == 1) { c = 640; }
if (p == 2) { c = 704; }
if (p == 3) { c = 768; }
if (p == 4) { c = 832; }
if (p == 5) { c = 896; }
if (p == 6) { c = 1024; }
if (p == 7) { c = 1088; }
}

for (int p = 0; p < 8; p++) // Top Right Tile.
{

if (p == 0) { c = 32; }
if (p == 1) { c = 96; }
if (p == 2) { c = 160; }
if (p == 3) { c = 224; }
if (p == 4) { c = 288; }
if (p == 5) { c = 352; }
if (p == 6) { c = 416; }
if (p == 7) { c = 480; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Right_ROW[p] >> N) & 1);

unsigned long Index = bs.to_ulong();
image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}

}

for (int p = 0; p < 8; p++) // Bottom Right Tile.
{
if (p == 0) { c = 480; }
if (p == 1) { c = 544; }
if (p == 2) { c = 608; }
if (p == 3) { c = 672; }
if (p == 4) { c = 736; }
if (p == 5) { c = 800; }
if (p == 6) { c = 864; }
if (p == 7) { c = 928; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Right_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}

}

if (X == 0)
{
encodeOneStep(filename, image, width, height);
}
}

return 0;

}
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Bitmaps are a bit unusual in that they order pixels from left to right starting from the bottom row, whereas most other formats start from the top. Mathematical plotting is like that too, where you go up the Y-axis as Y increases. In other words, to flip it vertically you'd have to reverse the order the rows are being processed.

I don't think there would be a problem with any of the BMP code as-is. The BMP headers it writes were identical to what I was using, so the only anomaly seems to be in your pixel data which is written all at once with a single write. It's probably wrong before that point, so the task is still to figure out where it's going wrong.

If you were eager to try something other than BMP, I was perhaps going to suggest the Netpbm format which is very simple to implement. Not all software works with it though.

PNG is a very good format and available everywhere, but the compression is involved enough that it's usually much easier to use an external library for it unless you can live with uncompressed pixel data. I see you've stumbled across lodepng which is a solid choice, so no problems there.

You generally won't need to place the copyright text for it in your own code since that would be present in the lodepng code itself. The license text is the zlib license which doesn't require you to do anything as long as you're not modifying the library or claiming you wrote it. The parts in your own code only call one or two functions from the library, which would mean you were using the library rather than rewriting any part of it, or at least that would be easier to claim if the comments weren't also copied.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Well I got the png to work... but I still get that error that I was getting with the bmp.

Again does anyone have ideas whats causing this?

Hey check this out. 1a is....
https://en.wikipedia.org/wiki/Substitute_character

Thats the issue!
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Problem Solved!
I'm surprised no one thought about it earlier...

Here is the working code.
All it does is extracts the letter A to a png file.
Code: Select all

#include "C:\Users\Chris\source\repos\PNG\lodepng.h"
#include "C:\Users\Chris\source\repos\PNG\lodepng.cpp"
#include <fstream>
#include <sstream>
#include <string>
#include <atlstr.h>
#include <iterator>
#include <iostream>
#include <bitset>
using namespace std;

//Encode from raw pixels to disk with a single function call
//The image argument has width * height RGBA pixels or width * height * 4 bytes
void encodeOneStep(const char* filename, std::vector<unsigned char>& image, unsigned width, unsigned height) {
//Encode the image
unsigned error = lodepng::encode(filename, image, width, height);

//if there's an error, display it
if (error) std::cout << "encoder error " << error << ": " << lodepng_error_text(error) << std::endl;
}

int main(int argc, char* argv[])
{
const unsigned width = 16, height = 16, no_elems = 4088;
std::vector<unsigned char> image(no_elems);

const char* filename = argc > 1 ? argv[1] : "test.png";

//  Pallette Kong Letters and Banana Sprites
static const unsigned char colours[16][4] = { // An array of colour values
//BBB  GGG  RRR AA
{ 255, 255, 255,255 }, /* White    */
{   64,  48,  0, 255 }, /* Index  1 */
{   112,  72, 0, 255 }, /* Index  2 */
{  168, 104, 56, 255 }, /* Index  3 */
{   216, 152, 0, 255 }, /* Index  4 */
{   248, 216, 8, 255 }, /* Index  5 */
{ 255, 255, 255, 255 }, /* Index  6 */
{ 208, 208, 208, 255 }, /* Index  7 */
{ 160, 160, 160, 255 }, /* Index  8 */
{ 112, 120, 120, 255 }, /* Index  9 */
{  72,   0,  0, 255 }, /* Index 10 */
{   160,   8, 8, 255 }, /* Index 11 */
{  248,  16, 16, 255 }, /* Index 12 */
{  248, 104, 8, 255 }, /* Index 13 */
{   0, 248,   0, 255 }, /* Index 14 */
{   0,   0,   0, 255 }  /* Index 15 */
};

char oData[0x6000]; // The Array Letter A
ifstream inFile;

inFile.open("C:\\Users\\Chris\\Desktop\\DK\\Emulators ad Debuggers\\zsnesw150\\dkc1.sfc", ios::binary); // Note. this opens a binary streem. This is needed. cuzz the program was using 0x1a values as a Sustitute AscII charter which halts the program/Using it as End Of File.
inFile.seekg(0x347519 + 10, 1);
inFile.close();

int E = 0;

int* Bitplane0_Top_Left_ROW = new int[8];
int* Bitplane1_Top_Left_ROW = new int[8];
int* Bitplane2_Top_Left_ROW = new int[8];
int* Bitplane3_Top_Left_ROW = new int[8];

int* Bitplane0_Top_Right_ROW = new int[8];
int* Bitplane1_Top_Right_ROW = new int[8];
int* Bitplane2_Top_Right_ROW = new int[8];
int* Bitplane3_Top_Right_ROW = new int[8];

int* Bitplane0_Bottom_Left_ROW = new int[8];
int* Bitplane1_Bottom_Left_ROW = new int[8];
int* Bitplane2_Bottom_Left_ROW = new int[8];
int* Bitplane3_Bottom_Left_ROW = new int[8];

int* Bitplane0_Bottom_Right_ROW = new int[8];
int* Bitplane1_Bottom_Right_ROW = new int[8];
int* Bitplane2_Bottom_Right_ROW = new int[8];
int* Bitplane3_Bottom_Right_ROW = new int[8];

for (int X = 0; X <= 40; X++)

{

// Letter K -- Top Left Tile
Bitplane0_Top_Left_ROW[0] = oData[14 + E];
Bitplane0_Top_Left_ROW[1] = oData[12 + E];
Bitplane0_Top_Left_ROW[2] = oData[10 + E];
Bitplane0_Top_Left_ROW[3] = oData[8 + E];
Bitplane0_Top_Left_ROW[4] = oData[6 + E];
Bitplane0_Top_Left_ROW[5] = oData[4 + E];
Bitplane0_Top_Left_ROW[6] = oData[2 + E];
Bitplane0_Top_Left_ROW[7] = oData[0 + E];

Bitplane1_Top_Left_ROW[0] = oData[15 + E];
Bitplane1_Top_Left_ROW[1] = oData[13 + E];
Bitplane1_Top_Left_ROW[2] = oData[11 + E];
Bitplane1_Top_Left_ROW[3] = oData[9 + E];
Bitplane1_Top_Left_ROW[4] = oData[7 + E];
Bitplane1_Top_Left_ROW[5] = oData[5 + E];
Bitplane1_Top_Left_ROW[6] = oData[3 + E];
Bitplane1_Top_Left_ROW[7] = oData[1 + E];

Bitplane2_Top_Left_ROW[0] = oData[30 + E];
Bitplane2_Top_Left_ROW[1] = oData[28 + E];
Bitplane2_Top_Left_ROW[2] = oData[26 + E];
Bitplane2_Top_Left_ROW[3] = oData[24 + E];
Bitplane2_Top_Left_ROW[4] = oData[22 + E];
Bitplane2_Top_Left_ROW[5] = oData[20 + E];
Bitplane2_Top_Left_ROW[6] = oData[18 + E];
Bitplane2_Top_Left_ROW[7] = oData[16 + E];

Bitplane3_Top_Left_ROW[0] = oData[31 + E];
Bitplane3_Top_Left_ROW[1] = oData[29 + E];
Bitplane3_Top_Left_ROW[2] = oData[27 + E];
Bitplane3_Top_Left_ROW[3] = oData[25 + E];
Bitplane3_Top_Left_ROW[4] = oData[23 + E];
Bitplane3_Top_Left_ROW[5] = oData[21 + E];
Bitplane3_Top_Left_ROW[6] = oData[19 + E];
Bitplane3_Top_Left_ROW[7] = oData[17 + E];

// Letter K -- Top Right Tile

Bitplane0_Top_Right_ROW[0] = oData[46 + E];
Bitplane0_Top_Right_ROW[1] = oData[44 + E];
Bitplane0_Top_Right_ROW[2] = oData[42 + E];
Bitplane0_Top_Right_ROW[3] = oData[40 + E];
Bitplane0_Top_Right_ROW[4] = oData[38 + E];
Bitplane0_Top_Right_ROW[5] = oData[36 + E];
Bitplane0_Top_Right_ROW[6] = oData[34 + E];
Bitplane0_Top_Right_ROW[7] = oData[32 + E];

Bitplane1_Top_Right_ROW[0] = oData[47 + E];
Bitplane1_Top_Right_ROW[1] = oData[45 + E];
Bitplane1_Top_Right_ROW[2] = oData[43 + E];
Bitplane1_Top_Right_ROW[3] = oData[41 + E];
Bitplane1_Top_Right_ROW[4] = oData[39 + E];
Bitplane1_Top_Right_ROW[5] = oData[37 + E];
Bitplane1_Top_Right_ROW[6] = oData[35 + E];
Bitplane1_Top_Right_ROW[7] = oData[33 + E];

Bitplane2_Top_Right_ROW[0] = oData[62 + E];
Bitplane2_Top_Right_ROW[1] = oData[60 + E];
Bitplane2_Top_Right_ROW[2] = oData[58 + E];
Bitplane2_Top_Right_ROW[3] = oData[56 + E];
Bitplane2_Top_Right_ROW[4] = oData[54 + E];
Bitplane2_Top_Right_ROW[5] = oData[52 + E];
Bitplane2_Top_Right_ROW[6] = oData[50 + E];
Bitplane2_Top_Right_ROW[7] = oData[48 + E];

Bitplane3_Top_Right_ROW[0] = oData[63 + E];
Bitplane3_Top_Right_ROW[1] = oData[61 + E];
Bitplane3_Top_Right_ROW[2] = oData[59 + E];
Bitplane3_Top_Right_ROW[3] = oData[57 + E];
Bitplane3_Top_Right_ROW[4] = oData[55 + E];
Bitplane3_Top_Right_ROW[5] = oData[53 + E];
Bitplane3_Top_Right_ROW[6] = oData[51 + E];
Bitplane3_Top_Right_ROW[7] = oData[49 + E];

// Letter K -- Bottom Left Tile

Bitplane0_Bottom_Left_ROW[0] = oData[78 + E];
Bitplane0_Bottom_Left_ROW[1] = oData[76 + E];
Bitplane0_Bottom_Left_ROW[2] = oData[74 + E];
Bitplane0_Bottom_Left_ROW[3] = oData[72 + E];
Bitplane0_Bottom_Left_ROW[4] = oData[70 + E];
Bitplane0_Bottom_Left_ROW[5] = oData[68 + E];
Bitplane0_Bottom_Left_ROW[6] = oData[66 + E];
Bitplane0_Bottom_Left_ROW[7] = oData[64 + E];

Bitplane1_Bottom_Left_ROW[0] = oData[79 + E];
Bitplane1_Bottom_Left_ROW[1] = oData[77 + E];
Bitplane1_Bottom_Left_ROW[2] = oData[75 + E];
Bitplane1_Bottom_Left_ROW[3] = oData[73 + E];
Bitplane1_Bottom_Left_ROW[4] = oData[71 + E];
Bitplane1_Bottom_Left_ROW[5] = oData[69 + E];
Bitplane1_Bottom_Left_ROW[6] = oData[67 + E];
Bitplane1_Bottom_Left_ROW[7] = oData[65 + E];

Bitplane2_Bottom_Left_ROW[0] = oData[94 + E];
Bitplane2_Bottom_Left_ROW[1] = oData[92 + E];
Bitplane2_Bottom_Left_ROW[2] = oData[90 + E];
Bitplane2_Bottom_Left_ROW[3] = oData[88 + E];
Bitplane2_Bottom_Left_ROW[4] = oData[86 + E];
Bitplane2_Bottom_Left_ROW[5] = oData[84 + E];
Bitplane2_Bottom_Left_ROW[6] = oData[82 + E];
Bitplane2_Bottom_Left_ROW[7] = oData[80 + E];

Bitplane3_Bottom_Left_ROW[0] = oData[95 + E];
Bitplane3_Bottom_Left_ROW[1] = oData[93 + E];
Bitplane3_Bottom_Left_ROW[2] = oData[91 + E];
Bitplane3_Bottom_Left_ROW[3] = oData[89 + E];
Bitplane3_Bottom_Left_ROW[4] = oData[87 + E];
Bitplane3_Bottom_Left_ROW[5] = oData[85 + E];
Bitplane3_Bottom_Left_ROW[6] = oData[83 + E];
Bitplane3_Bottom_Left_ROW[7] = oData[81 + E];

// Letter K -- Bottom Right Tile

Bitplane0_Bottom_Right_ROW[0] = oData[110 + E];
Bitplane0_Bottom_Right_ROW[1] = oData[108 + E];
Bitplane0_Bottom_Right_ROW[2] = oData[106 + E];
Bitplane0_Bottom_Right_ROW[3] = oData[104 + E];
Bitplane0_Bottom_Right_ROW[4] = oData[102 + E];
Bitplane0_Bottom_Right_ROW[5] = oData[100 + E];
Bitplane0_Bottom_Right_ROW[6] = oData[98 + E];
Bitplane0_Bottom_Right_ROW[7] = oData[96 + E];

Bitplane1_Bottom_Right_ROW[0] = oData[111 + E];
Bitplane1_Bottom_Right_ROW[1] = oData[109 + E];
Bitplane1_Bottom_Right_ROW[2] = oData[107 + E];
Bitplane1_Bottom_Right_ROW[3] = oData[105 + E];
Bitplane1_Bottom_Right_ROW[4] = oData[103 + E];
Bitplane1_Bottom_Right_ROW[5] = oData[101 + E];
Bitplane1_Bottom_Right_ROW[6] = oData[99 + E];
Bitplane1_Bottom_Right_ROW[7] = oData[97 + E];

Bitplane2_Bottom_Right_ROW[0] = oData[126 + E];
Bitplane2_Bottom_Right_ROW[1] = oData[124 + E];
Bitplane2_Bottom_Right_ROW[2] = oData[122 + E];
Bitplane2_Bottom_Right_ROW[3] = oData[120 + E];
Bitplane2_Bottom_Right_ROW[4] = oData[118 + E];
Bitplane2_Bottom_Right_ROW[5] = oData[116 + E];
Bitplane2_Bottom_Right_ROW[6] = oData[114 + E];
Bitplane2_Bottom_Right_ROW[7] = oData[112 + E];

Bitplane3_Bottom_Right_ROW[0] = oData[127 + E];
Bitplane3_Bottom_Right_ROW[1] = oData[125 + E];
Bitplane3_Bottom_Right_ROW[2] = oData[123 + E];
Bitplane3_Bottom_Right_ROW[3] = oData[121 + E];
Bitplane3_Bottom_Right_ROW[4] = oData[119 + E];
Bitplane3_Bottom_Right_ROW[5] = oData[117 + E];
Bitplane3_Bottom_Right_ROW[6] = oData[115 + E];
Bitplane3_Bottom_Right_ROW[7] = oData[113 + E];

E = E + 128 + 10;

int c = 0;

for (int p = 0; p < 8; p++) // Bottom Left Tile
{
if (p == 7) { c = 512; }
if (p == 6) { c = 576; }
if (p == 5) { c = 640; }
if (p == 4) { c = 704; }
if (p == 3) { c = 768; }
if (p == 2) { c = 832; }
if (p == 1) { c = 896; }
if (p == 0) { c = 960; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha
}

}

for (int p = 0; p < 8; p++) // Top Left Tile
{
if (p == 7) { c = 0; }
if (p == 6) { c = 64; }
if (p == 5) { c = 128; }
if (p == 4) { c = 192; }
if (p == 3) { c = 256; }
if (p == 2) { c = 320; }
if (p == 1) { c = 384; }
if (p == 0) { c = 448; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Left_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Left_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Left_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Left_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}
}

for (int p = 0; p < 8; p++) // bottom Right Tile. // not working yet
{
if (p == 7) { c = 512 +32; }
if (p == 6) { c = 576 + 32; }
if (p == 5) { c = 640 + 32; }
if (p == 4) { c = 704 + 32; }
if (p == 3) { c = 768 + 32; }
if (p == 2) { c = 832 + 32; }
if (p == 1) { c = 896 + 32; }
if (p == 0) { c = 960 + 32; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Bottom_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Bottom_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Bottom_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Bottom_Right_ROW[p] >> N) & 1);

unsigned long long Index = bs.to_ulong();
image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha

}

}

for (int p = 0; p < 8; p++) // top Right Tile.
{
if (p == 7) { c = 32; }
if (p == 6) { c = 96; }
if (p == 5) { c = 160; }
if (p == 4) { c = 224; }
if (p == 3) { c = 288; }
if (p == 2) { c = 352; }
if (p == 1) { c = 416; }
if (p == 0) { c = 480; }

for (int j = 0, N = 7; j < 8; j++, N--)
{
std::bitset<4> bs;
bs.set(0, (Bitplane0_Top_Right_ROW[p] >> N) & 1);
bs.set(1, (Bitplane1_Top_Right_ROW[p] >> N) & 1);
bs.set(2, (Bitplane2_Top_Right_ROW[p] >> N) & 1);
bs.set(3, (Bitplane3_Top_Right_ROW[p] >> N) & 1);
unsigned long Index = bs.to_ulong();

image[c++] = colours[Index][0]; // Red
image[c++] = colours[Index][1]; // Green
image[c++] = colours[Index][2]; // Blue
image[c++] = colours[Index][3]; // Alpha
}
}

if (X == 0)
{
encodeOneStep(filename, image, width, height);
}
}

return 0;

}

Kingizor do you have any advice on how to export sprites that have multiple tiles? I mean ones that have more then 16x16 tiles at various locations.
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

The various file functions provided by C and C++ will do newline substitution (0A <-> 0D 0A) if a file doesn't explicitly specify binary mode. Your BMP code uses CreateFile and WriteFile which are part of the Windows API and binary-only, so that wouldn't be an issue for them.

With files you should have a lot of error handling in your code because a lot of things can go wrong. With the BMP code, you can check the return value of WriteFile to find out whether it succeeded or not, and use the GetLastError function to obtain an error code if that is the case. There are functions in the Windows API to convert that to an error message, or you could just print the error code itself and look up the error too.

LodePNG shouldn't be encountering the same problem, as it certainly has its own file handling code and it's not reporting any errors.

I don't really see the significance of 0x1A as a character that would cause problems. The program should be able to write it without fault like any other character, and programs reading the resulting file should be able to read it as well.

Wait a minute! The ROM file is being opened using C++ iostream without specifying binary mode. That would cause it to muddle the new line characters when reading data from the file as described above.

Code: Select all
...
inFileB.open("C:\\path_to_rom\\dkc.sfc", std::ios::in | std::ios::binary); // good

Sorry for not noticing sooner. The BMP code I wrote specified binary, but I didn't think to check for that elsewhere. (not used to C++)
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

Oh, apparently we noticed the binary thing at the same time.

I've never actually done all that much with sprites in the games. I would image the way you would assemble them is the same as typical image composition, just decode individual tiles and composite them together after the fact. The calculations would involve the width and height of the tiles involved, the width and height of individual rows and columns, and some index indicating where you would want the tile to go. It should be possible to write a single function that could handle a variety of compositions, but it's probably better to handle things as you need to for the moment.

I'm sure there a handful of people who know more about the specifics of sprites in DKC than I do.
Trailblazer
75
Posts: 243
Joined: 2010

### Re: Sprite Graphics

I am getting some inconsistencies comparing the ingame sprite of the heart compared to the sprite viewer / extracted sprite from Simions extractor.

The heart in the ingame screenshot appears to be mirrored horizontally?

Why is this?
Attachments
553
Posts: 1188
Joined: 2008

### Re: Sprite Graphics

Cyclone wrote:The heart in the ingame screenshot appears to be mirrored horizontally?

Why is this?

Going left typically flips the sprite. Do you notice how pretty much all the sprites face right? The game programmatically flips these thereby not needing x flips of any sprites explicitly defined.
Treasure Hunter
93
Posts: 495
Joined: 2016

PreviousNext