#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <math.h>

#include "PICTFct.h"

#define PROCESSEUR_80x86                 1

#define TAILLE_ENTETE                    512
#define TAILLE_MAX_BLOC                  127
#define DEBUT_IMAGE                      634

#define VERSION_OPCODE                   0x0011
#define PICT_VERSION_2                   0x02FF
#define HEADER_OPCODE                    0x0C00
#define RESERVED_OPCODE                  0x009A
#define COMPACT_ADOBE_PHOTOSHOP          0x0004
#define PIXEL_32_BITS                    0x0020
#define PIXEL_3_COMPOSANTES              0x0003
#define COMPOSANTE_8_BITS                0x0008

void (*set_pixel)(int,int,struct color32 *) = NULL ;
void (*read_pixel)(int,int,struct color32 *) = NULL ;
void (*cherche_lligne)(int) = NULL ;
void (*sauve_lligne)(int) = NULL ;
void (*init_PICT)(int,int) = NULL ;
void (*fini_PICT)(void) = NULL ;

/* ************************************************************************ */

int lecture_entier_new(FILE *num)
  { short res;
#ifdef PROCESSEUR_80x86
    fread(((char *) (&res))+1,1,1,num) ;
    fread((char *) (&res),1,1,num) ;
#else
    fread(&res,2,1,num) ;
#endif
    return(res) ;
}
/* ------------------------------------------------------------------------ */
int sauvegarde_entier_new(FILE *num,short *entier)
  {
#ifdef PROCESSEUR_80x86
    fwrite(((char *) entier)+1,1,1,num) ;
    fwrite((char *) entier,1,1,num) ;
#else
    fwrite(&entier,2,1,num) ;
#endif
    return(2) ;
}
/* ------------------------------------------------------------------------ */
int sauvegarde_rectangle_new(FILE *num,short *xi,short *yi,short *xf,short *yf)
  { sauvegarde_entier_new(num,xi) ;
    sauvegarde_entier_new(num,yi) ;
    sauvegarde_entier_new(num,xf) ;
    sauvegarde_entier_new(num,yf) ;
    return(8) ;
}
/* ------------------------------------------------------------------------ */
void initgraphmode(void (*setp)(int,int,struct color32*),void (*getp)(int,int,struct color32*),void (*cherche_ligne_bt)(int),void (*sauve_ligne_bt)(int),void (*init)(int,int),void (*fini)(void))
  { set_pixel = setp ;
    read_pixel = getp ;
    cherche_lligne = cherche_ligne_bt ;
    sauve_lligne = sauve_ligne_bt ;
    init_PICT = init ;
    fini_PICT = fini ;
}
/* ------------------------------------------------------------------------ */
int construit_ligne(FILE *handle,struct ligne *l)
  { int           i,j,vv,ll ;
    signed char   longueur ;
    unsigned char *c,color ;
    int           erreur = 0 ;

    vv = 3 * l->dx ;
    if ( (c =(unsigned char *) calloc(vv,sizeof(unsigned char))) != NULL ) {
      if( l->dx < 63 )
        fseek(handle,1,SEEK_CUR) ;
        else
        fseek(handle,2,SEEK_CUR) ;
      i = 0;
      while ( i < vv ) {
        fread(&longueur,1,1,handle) ;
        ll = abs(longueur) + 1 ;
        if ( longueur < 0 ) {
          fread(&color,1,1,handle) ;
          for ( j = 0 ; j < ll ; j++ )
            c[i+j] = color ;
          i += ll ; }
          else {
          fread(&c[i],ll,1,handle) ;
          i += ll ; } }
      for ( i = 0 ; i < l->dx ; i++ ) {
        (l->p)[i].rouge = c[i] ;
        (l->p)[i].vert = c[l->dx + i] ;
        (l->p)[i].bleu = c[(l->dx << 1) + i] ; }
      free(c) ; }
      else
      erreur = -4 ;
    return(erreur) ;
}
/* ------------------------------------------------------------------------ */
void affiche_ligne(int x0,int x1,int  y,struct ligne *l)
  { int       i,resx ;

    resx = MIN(l->dx,x1 + 1) ;
    cherche_lligne(y) ;
    for ( i = 0 ; i < resx ; i++ ) {
      set_pixel(x0+i,y,&((l->p)[i])) ; }
    sauve_lligne(y) ;
}
/* ------------------------------------------------------------------------ */
int affiche_image_PICT24(char *fichier,int x0,int y0,int x1,int y1)
  { FILE *num ;
    int erreur ;
    int dx,dy,y,resy ;
    struct ligne l ;
    if ( ( set_pixel != NULL ) && ( read_pixel != NULL ) ) {
      if ( (num = fopen(fichier,"rb") ) != NULL) {
        if ( (erreur = verifie_PICT(num)) == 0 ) {
          fseek(num,0x00000206,SEEK_SET) ;
          dy = lecture_entier_new(num) ;
          dx = lecture_entier_new(num) ;
          l.dx = dx ;
          if ( (l.p =(struct color32 *) calloc((dx+10),sizeof(struct color32))) != NULL ) {
            init_PICT(dx,dy) ;
            fseek(num,DEBUT_IMAGE,SEEK_SET) ;
            resy = MIN(dy+y0,y1+1) ;
            for ( y = y0 ; y < resy ; y++ ) {
              if ( construit_ligne(num,&l) == 0 )
                affiche_ligne(x0,x1,y,&l) ;
                else
                return(-4) ; }
            fini_PICT() ;
            free(l.p) ; }
            else
            erreur = -3;
            fclose(num) ; } }
        else
        erreur = -2; }
      else
      erreur = -1;
    return(erreur) ;
}
/* ------------------------------------------------------------------------ */
unsigned sauvegarde_entete_new(FILE *num,short dx,short dy)
  { int           i;
    short         longueur;
    short         ll ;
    short         zero = 0x0000;
    unsigned char uc = 0x00;
    unsigned char version[4] = { 0x00,0x11,0x02,0xFF };
    unsigned char mess1[14]  = { 0x0C,0x00,0xFF,0xFE,0x00,0x00,0x00,0x48,
                                 0x00,0x00,0x00,0x48,0x00,0x00 };
    unsigned char mess2[8]   = { 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x0A };
    unsigned char mess3[6]   = { 0x00,0x9A,0x00,0x00,0x00,0xFF };
    unsigned char mess4[36] =  { 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
                                 0x00,0x48,0x00,0x00,0x00,0x48,0x00,0x00,
                                 0x00,0x10,0x00,0x20,0x00,0x03,0x00,0x08,
                                 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                 0x00,0x00,0x00,0x00 };
    unsigned char mess5[2]   = { 0x00,0x40 };

    for ( i = 0 ; i < TAILLE_ENTETE ; i++ )
      fwrite(&uc,1,1,num) ;
    ll = (dx * 4) | 0x8000;
    longueur =(short) sauvegarde_entier_new(num,&longueur) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) fwrite(version,4,1,num) ;
    longueur += (short) fwrite(mess1,14,1,num) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) fwrite(mess2,8,1,num) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) fwrite(mess3,6,1,num) ;
    longueur += (short) sauvegarde_entier_new(num,&ll) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) fwrite(mess4,36,1,num) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) sauvegarde_rectangle_new(num,&zero,&zero,&dy,&dx) ;
    longueur += (short) fwrite(mess5,2,1,num) ;
    return(longueur) ;
}
/* ------------------------------------------------------------------------ */
void lire_ligne(int x0,int x1,int y,struct ligne *l)
  { int      i,resx ;

    resx = MIN(l->dx,x1+1) ;
    cherche_lligne(y) ;
    for ( i = 0 ; i < resx ; i++ )
      read_pixel(x0+i,y,&l->p[i]) ;
}
/* ------------------------------------------------------------------------ */
unsigned sauve_ligne_compresse(FILE *num,struct ligne *l)
  { short         longueur,v2;
    int           i,nbpixbloc,nbpixligne,vv;
    unsigned char val1,val2,val3;
    signed char   marque;
    byte          *c = NULL,*d = NULL,v;

    vv = 3 * l->dx ;                                                                 /* Taille maximale du tableau des donn‚es brutes (composantes des pixels) */
    v2 =(short) (vv + (vv / 128 + 1) + 2) ;
    longueur = 0 ;
    if ( ( (c =(unsigned char *) calloc(vv+100,sizeof(unsigned char))) != NULL ) &&
         ( (d =(unsigned char *) calloc(v2+100,sizeof(unsigned char))) != NULL ) ) {
      for ( i = 0 ; i < l->dx ; i++ ) {
        c[i] = (l->p)[i].rouge ;
        c[l->dx + i] = (l->p)[i].vert ;
        c[(l->dx << 1) + i] = (l->p)[i].bleu ; }
      nbpixligne = 0 ;
      do {
        nbpixbloc  = 0 ;
        marque = 0 ;
        val1 = c[nbpixligne] ;
        if ( nbpixligne != (vv - 1) ) {
          val2 = c[nbpixligne+1] ;
          if ( val1 == val2 ) {
            do {
              marque-- ;
              nbpixbloc++ ;
              if ( ( (nbpixligne + nbpixbloc) != vv ) && ( nbpixbloc != TAILLE_MAX_BLOC ) )
                val2 = c[nbpixligne+nbpixbloc+1] ;
                else
                break ; }
            while( ( val1 == val2 ) && ( ((nbpixligne+nbpixbloc) % 128) != 0 ) ) ;
            nbpixbloc++ ;
            d[longueur] = marque ;
            longueur++ ;
            d[longueur] = val1 ;
            longueur++ ; }
          else {
          do {
            marque++ ;
            nbpixbloc++ ;
            val1 = val2 ;
            if( ( (nbpixligne + nbpixbloc) != vv ) && ( nbpixbloc != TAILLE_MAX_BLOC ) ) {
              val2 = c[nbpixligne+nbpixbloc+1] ;
              if ( val1 == val2 ) {
                if ( (nbpixligne + nbpixbloc + 1) != vv ) {
                  val3 = c[nbpixligne+nbpixbloc+2];
                  if ( val3 == val1 )
                    break ; } } }
              else
              break ; }
          while ( (nbpixligne+nbpixbloc) % 128 != 0 ) ;
          marque-- ;
          d[longueur] = marque ;
          longueur++ ;
          for ( i = 0 ; i < nbpixbloc ; i++ ) {
            d[longueur] = c[nbpixligne + i] ;
            longueur++ ; } } }
        else {
          marque = 0 ;
          nbpixbloc++ ;
          d[longueur] = marque ;
          longueur++ ;
          d[longueur] = c[nbpixligne] ;
          longueur++ ; }
        nbpixligne += nbpixbloc ; }
      while ( nbpixligne < vv ) ;
      if ( l->dx < 63 ) {
        v = (byte) longueur & 0x00FF ;
        fwrite(&v,1,1,num) ;
        fwrite(d,longueur,1,num) ;
        longueur++ ; }
        else {
        sauvegarde_entier_new(num,&longueur) ;
        fwrite(d,longueur,1,num) ;
        longueur += 2 ; } }
    if ( c )
      free(c) ;
    if ( d )
      free(d) ;
    return(longueur) ;
}
/* ------------------------------------------------------------------------ */
int sauvegarde_image_PICT24_new(char *fichier,int x0,int y0,int x1,int y1)
  { FILE            *num;
    int             erreur = 0 ;
    short           longueur,long_ligne ;
    int             y ;
    struct ligne    l ;
    byte            v ;
    unsigned char   fin[2] = { 0x00,0xFF } ;

    if ( ( set_pixel != NULL ) && ( read_pixel != NULL ) ) {
      if( (num = fopen(fichier,"wb") ) != NULL) {
        l.dx = x1 - x0 + 1 ;
        init_PICT(l.dx,y1-y0+1) ;
        longueur = (short) sauvegarde_entete_new(num,(short) l.dx,(short) (y1-y0+1)) ;
        if ( (l.p =(struct color32 *) calloc((l.dx+10),sizeof(struct color32))) != NULL ) {
          for ( y = y0 ; y <= y1 ; y++ ) {
            lire_ligne(x0,x1,y,&l) ;
            if ( (long_ligne = (short) sauve_ligne_compresse(num,&l)) != 0 )
              longueur += long_ligne ;
              else
              erreur = -5 ; }
          free(l.p) ; }
          else
          erreur = -4 ;
        if ( longueur % 2 ) {
          v = 0 ;
          longueur += (short) fwrite(&v,1,1,num) ; }
        if ( fwrite(fin,2,1,num) == 2 )
          longueur += 2;
          else
          erreur = -3 ;
        fseek(num,TAILLE_ENTETE,SEEK_SET) ;
        sauvegarde_entier_new(num,&longueur) ;
        fini_PICT() ;
        fclose(num) ; }
        else
        erreur = -2 ; }
      else
      erreur = -1 ;
    return(erreur) ;
}
/* ------------------------------------------------------------------------ */
void resolution_image(char *fichier,int *x,int *y)
  { FILE *num ;
    int erreur ;

    if ( (num = fopen(fichier,"rb")) != NULL ) {
      if ( (erreur = verifie_PICT(num)) == 0 ) {
        fseek(num,0x00000206,SEEK_SET) ;
        *y = lecture_entier_new(num) ;
        *x = lecture_entier_new(num) ;
        fclose(num) ; }
        else {
        *x = erreur ;
        *y = erreur ; } }
      else {
      *x = -1 ;
      *y = -1 ; }
}
/* ------------------------------------------------------------------------ */
int verifie_PICT(FILE *num)
  { int         erreur = 0 ;
    int         verif ;

    fseek(num,522,SEEK_SET) ;
    verif = lecture_entier_new(num) ;
    if ( verif == VERSION_OPCODE ) {
      verif = lecture_entier_new(num) ;
      if ( verif == PICT_VERSION_2 ) {
        if( (verif = lecture_entier_new(num)) != HEADER_OPCODE )
          erreur = -5 ; }
        else
        erreur = -5 ; }
      else
      erreur = -5 ;
    if ( erreur == 0 ) {
      fseek(num,564,SEEK_SET) ;
      verif = lecture_entier_new(num) ;
      if ( verif == RESERVED_OPCODE ) {
        fseek(num,582,SEEK_SET) ;
        if ( (verif = lecture_entier_new(num)) != COMPACT_ADOBE_PHOTOSHOP )
          erreur = -6; }
        else
        erreur = -6 ; }
    if ( erreur == 0 ) {
      fseek(num,598,SEEK_SET) ;
      verif = lecture_entier_new(num) ;
      if ( verif == PIXEL_32_BITS ) {
        verif = lecture_entier_new(num) ;
        if ( verif == PIXEL_3_COMPOSANTES ) {
          if ( (verif = lecture_entier_new(num)) != COMPOSANTE_8_BITS )
            erreur = -7 ; }
          else
          erreur = -7 ; }
        else
        erreur = -7 ; }
    return(erreur) ;
}

/* ************************************************************************ */
