Hello,

The FLTK functions are only allowing the complete loading of a png.

A while ago, i submitted the question of decoding an image which was 
already loaded in memory.
I use large resources files, and i can't use the FLTK legacy functions 
(all my images are in the same file).

So i wrote this, and post it here as there may be people interested.
It works only for PNG files, but i guess it's better than nothing.
You can compile it directly, you only need a png file for testing :)

For the png part; i used most of the FLTK code.



/* Decoding a PNG file loaded in memory.
   Last modification: 9 juin 2007

  windows: g++ jseb.cxx $(fltk-config --cflags --ldflags) -lfltk_png -
lfltk_z -lfltk_images 
  linux: same as above, but you have to just indicate this library "-
lfltk_images" 
*/


#include <FL/Fl.H>
#include <FL/Fl_Image.H>

#include <stdio.h>
//#include <config.h>
#include <png.h>
#include "zlib.h"

static void png_read_data_from_mem( png_structp png_ptr, //pointer on 
struct which contains addy on our source datas.
                                   png_bytep data,     //pointer on the 
dest buffer we have to fill
                                   png_size_t length); //nbr of bytes to 
read this time

struct Png_infos {
  char name_png[256];
  int offset_png; //where we stopped last time we read the "file"
  unsigned char *datas_png;
};

class Fl_PNG_Memory_Image : public Fl_RGB_Image
{
  public:
    Fl_PNG_Memory_Image(unsigned char *buffer_png, const char *name_png);
    int get_png_h() { return this->h(); }
    int get_png_w() { return this->w(); }
  private:
    Png_infos png_infos;
};


/* when we create the class, we have to pass a pointer on the beginning 
of the PNG file in memory
   we pass also the original name of the png, for error logging purpose 
*/
Fl_PNG_Memory_Image::Fl_PNG_Memory_Image (unsigned char *buffer_png, 
const char *name_png): Fl_RGB_Image(0,0,0) 
{
  int           i;                      // Looping var
  int           channels;               // Number of color channels
  png_structp   pp;                     // PNG read pointer
  png_infop     info;                   // PNG info pointers
  png_bytep     *rows;                  // PNG row pointers

  //prepare the struct
  png_infos.offset_png=0;
  png_infos.datas_png = buffer_png;


  // Setup the PNG data structures...
  pp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct(pp);

  if (setjmp(pp->jmpbuf))
  {
    Fl::warning("PNG file \"%s\" contains errors!\n", name_png);
    return;
  }

  // Initialize the function pointer to the PNG read "engine"...
//  png_set_read_fn (pp, (png_voidp) buffer_png, png_read_data_from_mem);
  png_set_read_fn (pp, (png_voidp) &png_infos, png_read_data_from_mem);

  //TODO : implémenter png_check_sig


  // Get the image dimensions and convert to grayscale or RGB...
  png_read_info(pp, info);

  if (info->color_type == PNG_COLOR_TYPE_PALETTE)
    png_set_expand(pp);

  if (info->color_type & PNG_COLOR_MASK_COLOR)
    channels = 3;
  else
    channels = 1;

  if ((info->color_type & PNG_COLOR_MASK_ALPHA) || info->num_trans)
    channels ++;

  w((int)(info->width));
  h((int)(info->height));
  d(channels);

  if (info->bit_depth < 8)
  {
    png_set_packing(pp);
    png_set_expand(pp);
  }
  else if (info->bit_depth == 16)
    png_set_strip_16(pp);

#  if defined(HAVE_PNG_GET_VALID) && defined(HAVE_PNG_SET_TRNS_TO_ALPHA)
  // Handle transparency...
  if (png_get_valid(pp, info, PNG_INFO_tRNS))
    png_set_tRNS_to_alpha(pp);
#  endif // HAVE_PNG_GET_VALID && HAVE_PNG_SET_TRNS_TO_ALPHA

  array = new uchar[w() * h() * d()];
  alloc_array = 1;

  // Allocate pointers...
  rows = new png_bytep[h()];

  for (i = 0; i < h(); i ++)
    rows[i] = (png_bytep)(array + i * w() * d());

  // Read the image, handling interlacing as needed...
  for (i = png_set_interlace_handling(pp); i > 0; i --)
    png_read_rows(pp, rows, NULL, h());

#ifdef WIN32
  // Some Windows graphics drivers don't honor transparency when RGB == 
white
  if (channels == 4) {
    // Convert RGB to 0 when alpha == 0...
    uchar *ptr = (uchar *)array;
    for (i = w() * h(); i > 0; i --, ptr += 4)
      if (!ptr[3]) ptr[0] = ptr[1] = ptr[2] = 0;
  }
#endif // WIN32

  // Free memory and return...
  delete[] rows;

  png_read_end(pp, info);
  png_destroy_read_struct(&pp, &info, NULL);
}


static void png_read_data_from_mem( png_structp png_ptr, //pointer on 
struct which contains pointer on our datas
                                    png_bytep data,  //where you have to 
copy the sources datas for libpng computing
                                    png_size_t length) //length of datas 
to copy
{
  Png_infos *pnginfo = (Png_infos *)png_get_io_ptr (png_ptr); //get the 
pointer on our struct
  unsigned char *src = &pnginfo->datas_png[pnginfo->offset_png];

  /* copy data from image buffer */
  memcpy (data, src, length);

  /* advance in the file */
  pnginfo->offset_png += length;
}

/* HERE ends the decoding part. For testing purpose, i wrote a test 
program for all this stuff */

/* test program */

#include <FL/Fl_Double_Window.H> //for the test program
#include <FL/Fl_PNG_Image.H> //for the test program
#include <malloc.h> //for the test program

class Image : public Fl_Double_Window
{
   public:
      Image(int x, int y, int width, int height, const char *title, const 
char *filename);
      Image(const char *title, const char *filename); //the window will 
have the png size.
      ~Image();
      virtual void draw();
   private:
      Fl_PNG_Memory_Image *png;
      unsigned char *buffer_png;
      int load_png(const char *filename);
}; 

 
/* if you want to specify size and coordinates of the window, use this 
constructor */
Image::Image(int x, int y, int width, int height, const char *title, 
const char *filename)
   : Fl_Double_Window ( x,y,width,height,title)
{
  this->png=0;
  this->box(FL_FLAT_BOX);
  this->color(FL_CYAN);

  load_png(filename);
  if (buffer_png) {
    png = new Fl_PNG_Memory_Image(buffer_png, filename);
  }
}


/* if you want to let the widget have the size of its png, and automatic 
position, use this constructor */
Image::Image(const char *title, const char *filename) : Fl_Double_Window 
(0,0,title)
{
  this->png=0;

  load_png(filename);
  if (buffer_png) {
    png = new Fl_PNG_Memory_Image(buffer_png, filename);
    resize(0, 0, png->get_png_w(), png->get_png_h());
  }

}

Image::~Image()
{
  if (png) delete png;
  delete buffer_png;
}

void Image::draw()
{
   if (png) { 
      //void draw(int x, int y)
      //Draws image (upper-left corner at x,y). This is the same as doing 
draw(x,y,img->w(),img->h(),0,0).
      png->draw(0,0);
   } 
}

int Image::load_png(const char *filename)
{
  int retour=0;
  FILE *hFile;

  hFile=fopen (filename,"rb");
  if (hFile) {
    fseek(hFile,0,SEEK_END);
    int filesize=ftell(hFile);
    fseek(hFile,0,SEEK_SET);
    buffer_png=new unsigned char[filesize];
    fread (buffer_png,sizeof(char),filesize,hFile);
    fclose(hFile);
  } else {
    fprintf(stderr,"Impossible d'ouvrir %s\n",filename);
    retour=-1;
  }
  return retour;
}


int main()
{
  Fl::visual(FL_RGB);
  
  Image *win = new Image(100,100,640,480,"Decoding png in memory", 
"file.png");
  Image *win_auto = new Image("Automatic resize", "file.png");
  win->show();
  win_auto->show();

  Fl::run();
  delete win;
  delete win_auto;
  return 0;
}


_______________________________________________
fltk mailing list
fltk@easysw.com
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to