/*
 * $Id: png.trm,v 1.2 1995/12/20 21:48:05 drd Exp $
 *
 */

/* GNUPLOT - png.trm */
/*
 * Copyright (C) 1995
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the modified code.  Modifications are to be distributed
 * as patches to released version.
 *
 * This software  is provided "as is" without express or implied warranty.
 *
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *  png
 *
 * AUTHORS
 *  Alexander Lehmann
 *                      derived from pbm.trm by Russell Lang
 *
 * send your comments or suggestions to (info-gnuplot@dartmouth.edu).
 *
 */

/* To compile this terminal driver, you need libpng and zlib, both are
   available at ftp://ftp.uu.net/graphics/png
*/

/* The following pbmplus drivers use the generic bit mapped graphics
   routines from bitmap.c to build up a bit map in memory.  The driver
   interchanges colomns and lines in order to access entire lines
   easily and returns the lines to get bits in the right order :
   (x,y) -> (y,XMAX-1-x). */
/* This interchange is done by calling b_makebitmap() with reversed
   xmax and ymax, and then setting b_rastermode to TRUE.  b_setpixel()
   will then perform the interchange before each pixel is plotted */

#ifndef GOT_DRIVER_H
#include "driver.h"
#endif

#ifdef TERM_REGISTER
register_term(png_driver)
#endif

#ifdef TERM_PROTO
TERM_PUBLIC void PNGoptions __P((void));
TERM_PUBLIC void PNGinit __P((void));
TERM_PUBLIC void PNGreset __P((void));
TERM_PUBLIC void PNGsetfont __P((void));
TERM_PUBLIC void PNGgraphics __P((void));
TERM_PUBLIC void PNGtext __P((void));
TERM_PUBLIC void PNGlinetype __P((int linetype));
TERM_PUBLIC void PNGpoint __P((unsigned int x, unsigned int y, int point));
#endif /* TERM_PROTO */

#ifdef TERM_BODY

#include "png.h"

/* make XMAX and YMAX a multiple of 8 */
#define PNG_XMAX (640)
#define PNG_YMAX (480)
#define PNG_VCHAR (FNT5X9_VCHAR)
#define PNG_HCHAR (FNT5X9_VCHAR)
#define PNG_VTIC FNT5X9_HBITS
#define PNG_HTIC FNT5X9_HBITS

static int png_font=1;  /* small font */
static int png_mode=0;  /* 0:monochrome 1:gray 2:color */

/* 7=black, 0=white */
static int png_gray[]={7,1,6,5,4,3,2,1,7};  /* grays  */
/* bit3=!intensify, bit2=!red, bit1=!green, bit0=!blue */
static int png_color_table[]={15,8,3,5,6,4,2,1,11,13,14}; /* colors */
static png_color png_palette[16];

void PNGoptions()
{
  png_font=1;
  png_mode=0;

  term_options[0]='\0';

  while (!END_OF_COMMAND) {
    if (almost_equals(c_token,"s$mall"))
      png_font=1;
    else if (almost_equals(c_token,"me$dium"))
      png_font=2;
    else if (almost_equals(c_token,"l$arge"))
      png_font=3;
    else if (almost_equals(c_token,"mo$nochrome"))
      png_mode=0;
    else if (almost_equals(c_token,"g$ray"))
      png_mode=1;
    else if (almost_equals(c_token,"c$olor"))
      png_mode=2;
    else {
      png_font=1; /* reset to default, since term is already set */
      png_mode=0;
      int_error("expecting: {small, medium, large} and {monochrome, gray, color}",c_token);
    }
    c_token++;
  }

  /* setup options string */

  switch(png_font) {
    case 1: strcat(term_options,"small"); break;
    case 2: strcat(term_options,"medium"); break;
    case 3: strcat(term_options,"large"); break;
  }

  switch(png_mode) {
    case 0: strcat(term_options," monochrome"); break;
    case 1: strcat(term_options," gray"); break;
    case 2: strcat(term_options," color"); break;
  }
}


void PNGinit()
{
#ifdef REOPEN_BINARY
   reopen_binary();
#endif /* REOPEN_BINARY */
}


void PNGreset()
{
#ifdef vms
   fflush_binary();
#endif /* vms */
}


void PNGsetfont()
{
  switch(png_font) {
    case 1:
      b_charsize(FNT5X9);
      term->v_char = FNT5X9_VCHAR;
      term->h_char = FNT5X9_HCHAR;
      term->v_tic = FNT5X9_HBITS;
      term->h_tic = FNT5X9_HBITS;
      break;
    case 2:
      b_charsize(FNT9X17);
      term->v_char = FNT9X17_VCHAR;
      term->h_char = FNT9X17_HCHAR;
      term->v_tic = FNT9X17_HBITS;
      term->h_tic = FNT9X17_HBITS;
      break;
    case 3:
      b_charsize(FNT13X25);
      term->v_char = FNT13X25_VCHAR;
      term->h_char = FNT13X25_HCHAR;
      term->v_tic = FNT13X25_HBITS;
      term->h_tic = FNT13X25_HBITS;
      break;
    }
}


void PNGgraphics()
{
  int numplanes;

  switch(png_mode) {
    case 0: numplanes=1; break;
    case 1: numplanes=3; break;
    case 2: numplanes=4; break;
  }

  PNGsetfont();
  /* rotate plot -90 degrees by reversing XMAX and YMAX and by
     setting b_rastermode to TRUE */
  b_makebitmap((unsigned int)(PNG_YMAX*ysize),
               (unsigned int)(PNG_XMAX*xsize),numplanes);
  b_rastermode = TRUE;

  if(png_mode!=0)
    b_setlinetype(0); /* solid lines */
}

void PNGtext()
{
  register int x,j,row;
  png_struct *png_ptr;
  png_info *info_ptr;
  register int i,value;
  png_bytef *prow;
  int mask, plane1, plane2, plane3, plane4;
  int red, green, blue;
  png_text pngtext;
  char text[100];
  extern char version[];
  extern char patchlevel[];

  png_ptr = malloc(sizeof (png_struct));
  if (!png_ptr) {
    b_freebitmap();
    return;
  }
  info_ptr = malloc(sizeof (png_info));
  if (!info_ptr) {
    free(png_ptr);
    b_freebitmap();
    return;
  }
  prow=malloc(b_ysize);
  if(!prow) {
    free(png_ptr);
    free(info_ptr);
    b_freebitmap();
    return;
  }

  if (setjmp(png_ptr->jmpbuf)) {
    png_write_destroy(png_ptr);
    free(png_ptr);
    free(info_ptr);
    free(prow);
    b_freebitmap();
    return;
  }

  png_info_init(info_ptr);
  png_write_init(png_ptr);

  png_init_io(png_ptr, outfile);

  info_ptr->width=b_ysize;
  info_ptr->height=b_xsize;

  info_ptr->bit_depth=png_mode==0 ? 1 : 4;
  info_ptr->color_type=png_mode==2 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
  if(png_mode==2) {
    info_ptr->valid |= PNG_INFO_PLTE;
    info_ptr->palette=png_palette;
    info_ptr->num_palette=16;
  }
  if(png_mode!=0) {
    info_ptr->valid |= PNG_INFO_sBIT;
    if(png_mode==1) {
      info_ptr->sig_bit.gray = 3;
      png_set_shift(png_ptr, &(info_ptr->sig_bit));
    } else {
      info_ptr->sig_bit.red = 2;
      info_ptr->sig_bit.green = 2;
      info_ptr->sig_bit.blue = 2;
    }
  }
  info_ptr->interlace_type=0;
  if(png_mode==0)
    png_set_invert_mono(png_ptr);

  if(png_mode==2)
    for(i=0;i<16;i++) {
      red = (i&4) ? 1 : 3;
      green = (i&2) ? 1 : 3;
      blue = (i&1) ? 1 : 3;
      if (i&8) {
        red--; green--; blue--;
      }
      png_palette[i].red=red*85;
      png_palette[i].green=green*85;
      png_palette[i].blue=blue*85;
    }

  sprintf(text, "gnuplot %sversion %s patchlevel %s", OS, version, patchlevel);

  pngtext.compression=-1;
  pngtext.key="Software";
  pngtext.text=text;
  pngtext.text_length=strlen(text);

  info_ptr->num_text=1;
  info_ptr->text=&pngtext;

  png_write_info(png_ptr, info_ptr);

  info_ptr->num_text=0;
  info_ptr->text=NULL;

  png_set_packing(png_ptr);

  /* dump bitmap in raster mode */
  for (x = b_xsize-1; x >= 0; x--) {
    row = (b_ysize/8)-1;
    for (j = row; j >= 0; j--) {
      mask = 0x80;
      plane1=(*((*b_p)[j]+x));
      if(png_mode!=0) {
        plane2=(*((*b_p)[j+b_psize]+x));
        plane3=(*((*b_p)[j+b_psize+b_psize]+x));
      } else {
        plane2=0;
        plane3=0;
      }
      if(png_mode==2)
        plane4=(*((*b_p)[j+b_psize+b_psize+b_psize]+x));
      else
        plane4=0;

      for (i=0; i<8; i++) {
        value=0;
        if (plane1 & mask)  value+=1;
        if (plane2 & mask)  value+=2;
        if (plane3 & mask)  value+=4;
        if (plane4 & mask)  value+=8;
        if(png_mode==1) value=7-value;

        prow[(row-j)*8+i]=(png_byte)value;
        mask>>=1;
      }
    }
    png_write_rows(png_ptr, &prow, 1);
  }

  png_write_end(png_ptr, info_ptr);
  png_write_destroy(png_ptr);

  free(png_ptr);
  free(info_ptr);
  free(prow);
  b_freebitmap();
}

void PNGlinetype(linetype)
int linetype;
{
  switch(png_mode) {
    case 0:
      b_setlinetype(linetype);
      break;
    case 1:
      if (linetype>=7)
        linetype %= 7;
      b_setvalue(png_gray[linetype+2]);
      break;
    case 2:
      if (linetype>=9)
        linetype %= 9;
      b_setvalue(png_color_table[linetype+2]);
      break;
  }
}

void PNGpoint(x,y,point)
unsigned int x,y;
int point;
{
  if(png_mode==0) line_and_point(x,y,point);
  else            do_point(x,y,point);
}

#endif /* TERM_BODY */

#ifdef TERM_TABLE

TERM_TABLE_START(png_driver)
      "png", "Portable Network Graphics [small medium large] [monochrome gray color]",
       PNG_XMAX, PNG_YMAX, PNG_VCHAR,
       PNG_HCHAR, PNG_VTIC, PNG_HTIC, PNGoptions,
       PNGinit, PNGreset, PNGtext, null_scale,
       PNGgraphics, b_move, b_vector, PNGlinetype,
       b_put_text, b_text_angle, null_justify_text, PNGpoint,
       do_arrow, set_font_null,
                        0, /* pointsize */
                        TERM_CAN_MULTIPLOT
TERM_TABLE_END(png_driver)

#undef LAST_TERM
#define LAST_TERM png_driver

#endif /* TERM_TABLE */

/*
 * NAME: png
 *
 * OPTIONS: small|medium|large (default small) <-- fontsize
 *	    monochrome|gray|colour (default monochrome)
 *
 * SUPPORTS: Portable Network Graphics 
 *
 * Further Info: Needs third party library:
 *
 * To compile this terminal driver, you need libpng and zlib, both are
 *  available at ftp://ftp.uu.net/graphics/png
 *
 */
