1
votes

I'm using the FreeImage 3.15.4 library to analyze PNG images. I'm basically trying to build a simple data structure of consisting of a palette of all colors as well as an array version of the image per-pixel data consisting of indexes into the palette.

The thing is that FreeImage_GetBits seems to be returning a pointer to invalid data and I'm not sure why. I am able to read the width and height of the PNG file correctly, but the data pointed to by FreeImage_GetBits is just garbage data, and appears to be of an odd size. No matter how many times I run the program, it consistently dies in the same place, when iPix in the code below is equal to 131740. I get a C0000005 error accessing bits[131740] in the std::find call. The actual and reported PNG image size is 524288.

Furthermore, I've tried this code with smaller images that I myself have built and they work fine. The PNG I'm using is provided my a third party, and does not appear to be corrupt in anyway (Photoshop opens it, and DirectX can process and use it normally)

Any ideas?

Here's the data declarations:

struct Color
{
    char b; // Blue
    char g; // Green
    char r; // Red
    char a; // Alpha value

    bool operator==( const Color& comp )
    {
        if ( a == comp.a &&
             r == comp.r &&
             g == comp.g &&
             b == comp.b )
            return TRUE;
        else
            return FALSE;
    }
};

typedef std::vector<Color> ColorPalette; // Array of colors forming a palette

And here's the code that does the color indexing:

// Read image data with FreeImage
unsigned int imageSize = FreeImage_GetWidth( hImage ) * FreeImage_GetHeight( hImage );

unsigned char* pData = new unsigned char[imageSize];

// Access bits via FreeImage
FREE_IMAGE_FORMAT fif;
FIBITMAP* hImage;
fif = FreeImage_GetFIFFromFilename( fileEntry.name.c_str() );
if( fif == FIF_UNKNOWN )
{
    return false;
}
hImage = FreeImage_Load( fif, filename );
BYTE* pPixelData = NULL;
pPixelData = FreeImage_GetBits( hImage );
if ( pPixelData == NULL )
{
    return false;
}

Color* bits = (Color*)pPixelData;
ColorPalette palette;

for ( unsigned int iPix = 0; iPix < imageSize; ++iPix )
{
    ColorPalette::iterator it;

    if( ( it = std::find( palette.begin(), palette.end(), bits[iPix] ) ) == palette.end() )
    {
        pData[iPix] = palette.size();
        palette.push_back( bits[iPix] );
    }
    else
    {
        unsigned int index = it - palette.begin();
        pData[iPix] = index;
    }
}
1
Are you sure that the format of the raw pixel data matches your structure? What if the order of the bytes are different? What if the compiler decides to add padding to your structure? - Some programmer dude
Also, if you are trying to access bits[524288] and the number of entries in bits is 524288, then you are accessing out of bounds as the top index should be 524287. - Some programmer dude
Sorry, that was a typo. It's failing at bits[131740], actually. I'll have to investigate the data again, since 131740 is suspiciously close to 524288 / 4 (8bpp instead of 32bpp perhaps?). I checked the data format returned by my smaller test image and worked out the code to process that format; it hadn't occurred to me that the format would differ between file types. How does FreeImage decide what format to store the internal data in? - 8bitcartridge

1 Answers

1
votes

The PNG images that were problematic were using indexed color modes and the raw pixel data was indeed being returned as 8bpp. The correct course of action was to treat this data as 8 bits per pixel, and treat each 8-bit value as an index into a palette of colors that can be retrieved using FreeImage_GetPalette. The alternative, which is the choice I ultimately made, was to call FreeImage_ConvertTo32Bits on these indexed color mode PNG images, and then pass everything through the same code path as the same 32-bit image format.

Pretty simple conversion, but here it is:

// Convert non-32 bit images
if ( FreeImage_GetBPP( hImage ) != 32 )
{
    FIBITMAP* hOldImage = hImage;
    hImage = FreeImage_ConvertTo32Bits( hOldImage );
    FreeImage_Unload( hOldImage );
}