Jump to content
  • Advertisement
Sign in to follow this  
BlackJoker

C# Decode ICO file manually

Recommended Posts

Posted (edited)

Hi to all.

Currently I am trying to decode ICO file with my own loader, but I am getting weird results.

Here is screen from debugger.

image.png.213d34e300a254815e7cb3f56dc992d0.png

I have icon 170*170 px, with 32bit color, but in header info I see that height is twice bigger. It will be ok if image size also correspond to the size, but It does not.

170*170*4 = 115 600

170*340*4 = 231 200

But image size is 119 680.

After I read 115 600, there are 4080 bytes array, which is mostly filled with zeroes.

If I understood correctly, this is transparency buffer. I applied it, but in any case colors does not change.

Here is how I read colors data:

int realBitsCount = (int)iconImageInfo.Header.bitCount;
bool hasAndMask = /*(realBitsCount < 32) &&*/ (height != iconImageInfo.Header.height);

dataPtr = IntPtr.Add(dataPtr, Marshal.SizeOf<IconImageInfo>());
var buffer = new byte[width * height * 4];

var stream = new UnmanagedMemoryStream((byte*)dataPtr, iconInfo.BytesInRes);
var sizeinBytes = realBitsCount / 8;
int bufferOffset = 0;
if (realBitsCount == 32)
{
  for (int i = 0; i < buffer.Length; i += sizeinBytes)
  {
    stream.Read(buffer, bufferOffset, sizeinBytes);
    Utilities.Swap(ref buffer[bufferOffset], ref buffer[bufferOffset + 2]);
    bufferOffset += sizeinBytes;
  }
}

and here is how I read and apply transparency:

int shift;
int shift2;
byte bit;
int mask;

int boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico)
while (boundary % 32 != 0) boundary++;

boundary = width;
while (boundary % 32 != 0) boundary++;

var bufSize = iconInfo.BytesInRes - (width * height * (realBitsCount / 8));
var transparencyBuffer = new byte[bufSize];
stream.Read(transparencyBuffer, 0, (int)bufSize);
for (int y = 0; y < height; y++)
{
  for (int x = 0; x < width; x++)
  {
    shift = 4 * (x + y * width) + 3;
    bit = (byte)(7 - (x % 8));
    shift2 = (x + (height - y - 1) * boundary) / 8;
    var b = transparencyBuffer[shift2];
    mask = (0x01 & (b >> bit));
    var before = buffer[shift];
    buffer[shift] *= (byte)(1 - mask);
  }
}

And here is what I receive and what I should receive (from left to right)

image.png.1ed7c61e70f3beae5b66adaf54b992bc.pngimage.png.5048cf7190df77796b09dee2cd0fb250.png      

Does anyone know what I am doing wrong here? Please, help me.

 

Edited by BlackJoker

Share this post


Link to post
Share on other sites
Advertisement

It looks like you have your RGBA order mixed up.  If I recompose the channels from your final image, I get the source image.  Your final image seemed to be ordered more like GARB instead of RGBA.  When I recompose, I selected:

R->G
G->A
B->R
A(nothing)->B
 

rgba-compose.png

Share this post


Link to post
Share on other sites

@xycsoscyx

Very interesting. I thought that colors inside ico is in BGRA format.... Its very very strange. Need to check. Thanks for aimingю

Share this post


Link to post
Share on other sites
Posted (edited)

I tried with another ico and found that there is also RGB, AND and XOR masks (There are no such data for the icon above). Color actually not correct, but seems these color should be changed by this masks somehow. Does anyone know how to correctly apply this masks?

image.png.aa3997961a2d82f1d321d64017dc5ed3.png

Edited by BlackJoker

Share this post


Link to post
Share on other sites

@xycsoscyx

Regarding channels - I tried various combinations, but none of them give me at least nearly the same colors as in the original ico file. 

Share this post


Link to post
Share on other sites

BTW, one more interesting things I found: If Ico file has 24 bit color depth, colors are good, but if it has 32 bit -> colors are messed up.

Share this post


Link to post
Share on other sites

Simply: you are using the wrong data types, and have other bugs in your parser.  You don't show enough code, but there are several bugs clearly visible.

The screenshot shows a height of 340.  The ICO file format uses a BYTE to store the height.  Any value greater than 255 is a bug.

Specifically it contains this structure for each icon in the file:

 typedef struct tagICONDIRENTRY      
 {      
     BYTE bWidth;      
     BYTE bHeight;      // <<---- Cannot be greater than 255 in an ICO file.
     BYTE bColorCount;      
     BYTE bReserved;      
     WORD wPlanes;      
     WORD wBitCount;      
     DWORD dwBytesInRes;      
     DWORD dwImageOffset;      
 }ICONDIRENTRY, *LPICONDIRENTRY;   

 

We can't know that without knowing your actual code, but I note from your screen shot, you don't even know the actual data types because you are using var types that are deduced from context.  Maybe they're the right ones, maybe they're not. Based on what came up in the hover, it probably isn't the right type.

Based on what fields are showing, it looks like you've got a bitmap header rather than an icon header. If your code attempted to read the wrong structure from the file, of course it will not match the data in the file because it doesn't match what the file actually uses.

When working with file formats like this, you cannot take the lazy approach of deduced implicit types.  Working with file formats you must either use a byte array directly or explicitly use the correct struct which is properly packed and ordered. Using var for this sort of thing leads to bugs when you force the compiler to guess your intention.

Share this post


Link to post
Share on other sites

@frob

Actually you are right. I found out that last structure I am reading should be 40 bytes instead of 46 bytes. So, this 6 bytes were producing all there errors. Seems now ico files reading correctly.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!