gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > libpng and Reading PNG8 files (SOLVED)

#148011 - rook02 - Mon Dec 31, 2007 6:01 pm

Hello, and Happy New Year to everyone.

I'm new to DS programming, and I'm trying to work on a making a png8 file reader using libpng and libfat. I want to be able to read pixel data from the png and load them directly into the OAM.

I've been using the libpng manual and I've managed to get as far as being able to get my DS read the png header and give me the width and height of the image. I'm having problems, however with actually getting the pixel data. I'm getting either invalid window size errors (using png_read_image) or invalid chunks (using png_read_png).

Can anyone help me figure out what I'm doing wrong?


Last edited by rook02 on Fri Jan 25, 2008 8:05 pm; edited 1 time in total

#148012 - Peter - Mon Dec 31, 2007 6:14 pm

Hi,

why would you use png files and not nds raw image data?

I think decoding of png files can consume quite some time. You probably don't want to decode a png every time you load it to vram, so it must lie decoded in memory all the time anyway.

Personally I don't see an avantage of png's over raw image data.
_________________
Kind Regards,
Peter

#148018 - Quirky - Mon Dec 31, 2007 7:56 pm

PNG8 are indexed PNGs, right? if so, you need something like this:
Code:

if(info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

  png_colorp png_palette;
  int palette_entries;
  png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
  int paletteSize = palette_entries;
  unsigned short * palette = (unsigned short*)malloc(sizeof(unsigned short) * palette_entries);
  for (int i = 0; i < palette_entries; ++i)
  {
    palette[i] = RGB8(png_palette[i].red, png_palette[i].green, png_palette[i].blue);
  }
}

int number_passes = png_set_interlace_handling(png_ptr);
png_bytep rowBuffer = (png_bytep)malloc(sizeof(png_bytep) * info_ptr->rowbytes);
for (int pass = 0; pass < number_passes; pass++)

  for (unsigned int line = 0; line < height; line++)
  {
    png_read_row(png_ptr, rowBuffer, png_bytep_NULL);
    renderLine((const unsigned char*)rowBuffer, line);
  }



This is copied from my own code, modified a bit for an example. It uses mostly the same nomenclature as the libpng examples, so should make sense.

You have to read the palette in then read the data. This uses the read row technique, but changing it to read the whole image shouldn't be tricky. The toughest part now is getting it to the correct format that VRAM expects. Sprite ram is particularly irritating to get right, as you need to load it into the sprite "tiles" taking into account the size of your sprite, rather than just copying each line from the PNG as is.

I'd recommend using grit to get the data format right first, then once that's working try for reading in PNGs. And I guess the advantage of not using converted data is that it is faster to test new images.

#148041 - rook02 - Tue Jan 01, 2008 7:41 am

Thanks for the reply.

I tried the code (I'm getting an undefined reference for renderLine though), but it seems that everytime I call png_read_row() or png_read_png(), I get a "libpng error invalid window size". I'm not sure what's causing it. Did I miss a significant step? I don't recall any of the libpng tutorials saying anything about window sizes.

#148044 - Quirky - Tue Jan 01, 2008 11:51 am

rook02 wrote:
Thanks for the reply.

I tried the code (I'm getting an undefined reference for renderLine though), but it seems that everytime I call png_read_row() or png_read_png(), I get a "libpng error invalid window size". I'm not sure what's causing it. Did I miss a significant step? I don't recall any of the libpng tutorials saying anything about window sizes.


renderLine is a function that you should implement to render the line somewhere (or copy it into a big buffer of data).

I searched my HD and found an easier example that I was messing with. I've cleaned it up a bit here. It reads a file with palette information and saves it to the global char* data. The original was in C++ with a stack object that handled file IO (RAII), so this example doesn't close the FILE correctly on return. Compile it for your PC and if it works with your image, then it should work on the DS too. Or at least help you debug stuff. Note the comment about compiling libpng with NO_STIO defined - this saves space on the DS due to not including the error macros in the png library that use sprintf IIRC, but you have to use your own read function.

Code:

#include  <png.h>
#include <stdio.h>
#include <malloc.h>

#define RGB8(r, g, b) 0

unsigned short * palette = 0;
unsigned char * data = 0;
static void user_read_fn(png_structp png_ptr, unsigned char *dest, png_size_t size)
{
  FILE * f = (FILE*)png_get_io_ptr(png_ptr);
  size_t n = fread((char*)dest, size, 1, f);
  if(size && (n == 0)) {
    printf("Read error: invalid or corrupted PNG file\n");
  }
}

void readPng(const char * filename)
{
   FILE * f = 0;
   f = fopen(filename, "rb");
   if (!f) { return; }

   png_structp png_ptr= png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   if (png_ptr == NULL) { return; }

   /* Allocate/initialize the memory for image information.  REQUIRED. */
   png_infop info_ptr = png_create_info_struct(png_ptr);
   if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); return; }

   /* Set error handling if you are using the setjmp/longjmp method (this is
    * the normal method of doing things with libpng).  REQUIRED unless you
    * set up your own error handlers in the png_create_read_struct() earlier.
    */
   if (setjmp(png_jmpbuf(png_ptr))) {
      /* Free all of the memory associated with the png_ptr and info_ptr */
      png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
      /* If we get here, we had a problem reading the file */
      printf("problem reading the file\n");
      return;
   }

   // compiled png with NO_STIO - avoids sprintf madness.
   png_set_read_fn(png_ptr, (void *)f, user_read_fn);
   // read the easy way
   png_read_info(png_ptr, info_ptr);
   // alpha images require this:
   png_set_strip_alpha(png_ptr);

   if(info_ptr->color_type == PNG_COLOR_TYPE_GRAY && info_ptr->bit_depth < 8)
   {
     printf("Handle gray 1,2,4\n");
     //png_set_gray_1_2_4_to_8(png_ptr);
     png_set_expand(png_ptr);
   }

   if (info_ptr->bit_depth < 8) { printf("Expand\n"); png_set_packing(png_ptr); }
   // paletted images require this:
   if(info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
   {
     printf("Handle palette\n");
#if 0
     // remove palette info
     png_set_palette_to_rgb(png_ptr);
#else
     png_colorp png_palette;
     int palette_entries;
     png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
     printf(" %d palette entries\n", palette_entries);
     palette = (unsigned short*)malloc(sizeof(unsigned short) * palette_entries);
     int i;
     for (i = 0; i < palette_entries; ++i)
     {
       palette[i] = RGB8(png_palette[i].red, png_palette[i].green, png_palette[i].blue);
     }
#endif
   }
   printf("handle interlace \n");
   png_set_interlace_handling(png_ptr);
   printf("update info \n");
   png_read_update_info(png_ptr, info_ptr);
   printf("Extract data\n");
   int height = info_ptr->height;
   data = (unsigned char*)malloc( info_ptr->rowbytes * height);
   png_bytep * row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);
   if (row_pointers == 0)
   {
     printf("OUT OF MEM!\n");
     return;
   }
   int y;
   for (y = 0; y < height; y++)
   {
     row_pointers[y] = (png_bytep)(data + (info_ptr->rowbytes * y));
   }

   printf("read image\n");
   png_read_image(png_ptr, row_pointers);

   // clear up row pointers - now everything is in data
   printf("free pointer\n");
   free(row_pointers);
   printf("destroy read struct\n");
   png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
}

int main(int argc, char * argv[])
{
  readPng(argv[1]);
  return 0;
}                                                           

#148048 - rook02 - Tue Jan 01, 2008 2:31 pm

Finally figured it out... there wasn't anything wrong with the code. There were just some things in zlib that weren't configured right. Now everything works. I just need to work on a way to format the data the way the OAM wants it.

Thanks for the help. :D

#151303 - SiW - Fri Feb 22, 2008 11:25 pm

[ nevermind, unrelated problem ]