Re: Animated screenshot and gif encoder...



Hi !


Finally I have made a working code to create gif and add frame to it.
There's a lot of bugs currently (no time to correct it at the moment).

I give it to you as example if anybody is looking for something similar.

That's the code I've created to solve my problem :


void screenshot_anim_create_nostatic(GLOBAL_SKIN_INFOS* gsi) {


    /* Magic number for Gif file format */
        static char magic_number[] = {'G', 'I', 'F', '8', '9', 'a'};

    /* Size of canvas width on 2 bytes, heigth on 2 bytes, GCT , bg color i
(place dans la palette ici c'est blanc) , aspect pixel ratio */
        static char canvas[] = { 96, 0, 64, 0, 0x80, 0x0f, 0x00};

    /* This header contains a lot of things : animation : NETSCAPE2.0. And a
comment */
    static char header[31] = {
        0x21, 0xff, 0x0b, 'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.',
'0', 3, 1, 0, 0, 0,
    0x21, 0xfe, 8, 'T', 'i', 'l', 'E', 'm', '2', 0, 0, 0    };

    /* GCT, multiple de 2 */
        static char palette[] = { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00};

    /* Extension block introduced by 0x21 ('!'), and an img introduced by
0x2c (',') followed by coordinate corner(0,0), canvas 4 bytes, no local
color table */
    static char sub_header[18] = {0x21, 0xf9, 4, 5, 11, 0, 0xff, 0, 0x2c, 0,
0, 0, 0, 96, 0, 64, 0, 0};

    /* Get the pix into my appli */
    int width, height;
    guchar* lcddata;
    int x, y;
    width = gsi->emu->calc->hw.lcdwidth;
    height = gsi->emu->calc->hw.lcdheight;
    /* Alloc mmem */
    lcddata = g_new(guchar, (width / 8) * height);

    /* Get the lcd content using the function 's pointer from Benjamin's
core */
    (*gsi->emu->calc->hw.get_lcd)(gsi->emu->calc, lcddata);




    gsi->animation_file = fopen("gifencod.gif", "w");

    fwrite(magic_number, 6, 1, gsi->animation_file);
    fwrite(canvas, 7, 1, gsi->animation_file);
    fwrite(header, 31, 1, gsi->animation_file);
    fwrite(palette, 6, 1, gsi->animation_file);
    fwrite(sub_header, 18, 1, gsi->animation_file);

    long i= 0;

    unsigned char q[(96*64)];

    /* Ugly hack to convert to convert to black and white */
    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            if (lcddata[(y * width + x) / 8] & (0x80 >> (x % 8))) {
                //printf("i = %d", i);
                q[i] = 0x01;
                i++;
            } else {
                q[i] = 0x00;
                i++;
            }
        }
    }


    /* Use external file based on whirlgif to encodei (LZW cryption) body of
gif */
    GifEncode(gsi->animation_file, q , 1, (96*64));
}


/* Add a frame to an existing gif file */
void screenshot_anim_addframe(GLOBAL_SKIN_INFOS* gsi) {

    /* Some specific thing from my appli */
    int width, height;
    guchar* lcddata;
    int x, y;
    long i= 0;
    /* q will receive a modified copy of lcddata (lcddata comes from core
directly) */
    unsigned char q[(96*64)];
    width = gsi->emu->calc->hw.lcdwidth;
    height = gsi->emu->calc->hw.lcdheight;

    /* Alloc mmem */
    lcddata = g_new(guchar, (width / 8) * height);

    /* Get the lcd content using the function 's pointer from Benjamin's
core */
    (*gsi->emu->calc->hw.get_lcd)(gsi->emu->calc, lcddata);


    /* Extension block introduced by 0x21 ('!'), and an img introduced by
0x2c (',') followed by coordinate corner(0,0), canvas 4 bytes, no local
color table */
    static char sub_header[18] = {0x21, 0xf9, 4, 5, 11, 0, 0x0f, 0, 0x2c, 0,
0, 0, 0, 96, 0, 64, 0, 0};

    /* The end value */
       char end[1] = { 0xC3};

        fwrite(sub_header, 18, 1, gsi->animation_file);


    /* Format it for my black and white palette */
    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            if (lcddata[(y * width + x) / 8] & (0x80 >> (x % 8))) {
                //printf("i = %d", i);
                q[i] = 0x01;
                i++;
            } else {
                q[i] = 0x00;
                i++;
            }
        }
    }

    /* Use external file to encode (LZW cryption) body of gif */
    GifEncode(gsi->animation_file, q , 1, (96*64));

    /* Write block end */
    fwrite(end, 1, 1,gsi->animation_file);
}


And here is the external file which was used ...

I finally use gifencod.c from Hans Dinsen-Hansen and  Michael A. Mayer
(inspired by gifcode.c) :

/*
 * gifencode.c
 *
 * Copyright (c) 1997,1998 by Hans Dinsen-Hansen
 * The algorithms are inspired by those of gifcode.c
 * Copyright (c) 1995,1996 Michael A. Mayer
 * All rights reserved.
 *
 * This software may be freely copied, modified and redistributed
 * without fee provided that above copyright notices are preserved
 * intact on all copies and modified copies.
 *
 * There is no warranty or other guarantee of fitness of this software.
 * It is provided solely "as is". The author(s) disclaim(s) all
 * responsibility and liability with respect to this software's usage
 * or its effect upon hardware or computer systems.
 *
 * The Graphics Interchange format (c) is the Copyright property of
 * Compuserve Incorporated.  Gif(sm) is a Service Mark property of
 * Compuserve Incorporated.
 *
 *
 *           Implements GIF encoding by means of a tree search.
 *           --------------------------------------------------
 *
 *  - The string table may be thought of being stored in a "b-tree of
 * steroids," or more specifically, a {256,128,...,4}-tree, depending on
 * the size of the color map.
 *  - Each (non-NULL) node contains the string table index (or code) and
 * {256,128,...,4} pointers to other nodes.
 *  - For example, the index associated with the string 0-3-173-25 would be
 * stored in:
 *       first->node[0]->node[3]->node[173]->node[25]->code
 *
 *  - Speed and effectivity considerations, however, have made this
 * implementation somewhat obscure, because it is costly to initialize
 * a node-array where most elements will never be used.
 *  - Initially, a new node will be marked as terminating, TERMIN.
 * If this node is used at a later stage, its mark will be changed.
 *  - Only nodes with several used nodes will be associated with a
 * node-array.  Such nodes are marked LOOKUP.
 *  - The remaining nodes are marked SEARCH.  They are linked together
 * in a search-list, where a field, NODE->alt, points at an alternative
 * following color.
 *  - It is hardly feasible exactly to predict which nodes will have most
 * used node pointers.  The theory here is that the very first node as
 * well as the first couple of nodes which need at least one alternative
 * color, will be among the ones with many nodes ("... whatever that
 * means", as my tutor in Num. Analysis and programming used to say).
 *  - The number of possible LOOKUP nodes depends on the size of the color
 * map.  Large color maps will have many SEARCH nodes; small color maps
 * will probably have many LOOKUP nodes.
*/

#ifndef gif_encod_header
#include "gifencod.h"
#endif

#define BLOKLEN 255
#define BUFLEN 1000




int GifEncode(FILE *fout, unsigned char *pixels, int depth, int siz){
  printf("1\n");
  GifTree *first = &GifRoot, *newNode, *curNode;
  UBYTE   *end;
  int     cc, eoi, next, tel=0;
  short   cLength;

  char    *pos, *buffer;

  empty[0] = NULL;
  need = 8;

  nodeArray = empty;
  memmove(++nodeArray, empty, 255*sizeof(GifTree **));
  if (( buffer = (char *)malloc((BUFLEN+1)*sizeof(char))) == NULL )
  buffer++;


  pos = buffer;
  buffer[0] = 0x0;

  cc = (depth == 1) ? 0x4 : 1<<depth;
  fputc((depth == 1) ? 2 : depth, fout);
  eoi = cc+1;
  next = cc+2;

  cLength = (depth == 1) ? 3 : depth+1;
  printf("2\n");

  if (( topNode = baseNode = (GifTree *)malloc(sizeof(GifTree)*4094)) ==
NULL )
    return -1;
    //TheEnd1("No memory for GIF-code tree");
  if (( nodeArray = first->node = (GifTree **)malloc(256*sizeof(GifTree
*)*noOfArrays)) == NULL )
    printf("error");
    //TheEnd1("No memory for search nodes");
  lastArray = nodeArray + ( 256*noOfArrays - cc);
  ClearTree(cc, first);

  pos = AddCodeToBuffer(cc, cLength, pos);

  end = pixels+siz;
  curNode = first;
  while(pixels < end) {

    if ( curNode->node[*pixels] != NULL ) {
      curNode = curNode->node[*pixels];
      tel++;
      pixels++;
      chainlen++;
      continue;
    } else if ( curNode->typ == SEARCH ) {
      newNode = curNode->nxt;
      while ( newNode->alt != NULL ) {
    if ( newNode->ix == *pixels ) break;
    newNode = newNode->alt;
      }
      if (newNode->ix == *pixels ) {
    tel++;
    pixels++;
    chainlen++;
    curNode = newNode;
    continue;
      }
    }

/* ******************************************************
 * If there is no more thread to follow, we create a new node.  If the
 * current node is terminating, it will become a SEARCH node.  If it is
 * a SEARCH node, and if we still have room, it will be converted to a
 * LOOKUP node.
*/
  newNode = ++topNode;
  switch (curNode->typ ) {
   case LOOKUP:
     newNode->nxt = NULL;
     newNode->alt = NULL,
     curNode->node[*pixels] = newNode;
   break;
   case SEARCH:
     if ( nodeArray != lastArray ) {
       nodeArray += cc;
       curNode->node = nodeArray;
       curNode->typ = LOOKUP;
       curNode->node[*pixels] = newNode;
       curNode->node[(curNode->nxt)->ix] = curNode->nxt;
       lookuptypes++;
       newNode->nxt = NULL;
       newNode->alt = NULL,
       curNode->nxt = NULL;
       break;
     }
/*   otherwise do as we do with a TERMIN node  */
   case TERMIN:
     newNode->alt = curNode->nxt;
     newNode->nxt = NULL,
     curNode->nxt = newNode;
     curNode->typ = SEARCH;
     break;
   default:
     fprintf(stderr, "Silly node type: %d\n", curNode->typ);
  }
  newNode->code = next;
  newNode->ix = *pixels;
  newNode->typ = TERMIN;
  newNode->node = empty;
  nodecount++;
  printf("4\n");
/*
* End of node creation
* ******************************************************
*/
  if (debugFlag) {
    if (curNode == newNode) fprintf(stderr, "Wrong choice of node\n");
    if ( curNode->typ == LOOKUP && curNode->node[*pixels] != newNode )
fprintf(stderr, "Wrong pixel coding\n");
    if ( curNode->typ == TERMIN ) fprintf(stderr, "Wrong Type coding; frame
no = %d; pixel# = %d; nodecount = %d\n", count, tel, nodecount);
  }
    pos = AddCodeToBuffer(curNode->code, cLength, pos);
    if ( chainlen > maxchainlen ) maxchainlen = chainlen;
    chainlen = 0;
    if(pos-buffer>BLOKLEN) {
      buffer[-1] = BLOKLEN;
      fwrite(buffer-1, 1, BLOKLEN+1, fout);
      buffer[0] = buffer[BLOKLEN];
      buffer[1] = buffer[BLOKLEN+1];
      buffer[2] = buffer[BLOKLEN+2];
      buffer[3] = buffer[BLOKLEN+3];
      pos -= BLOKLEN;
    }
    curNode = first;

    if(next == (1<<cLength)) cLength++;
    next++;

    if(next == 0xfff) {
      ClearTree(cc,first);
      pos = AddCodeToBuffer(cc, cLength, pos);
      if(pos-buffer>BLOKLEN) {
    buffer[-1] = BLOKLEN;
    fwrite(buffer-1, 1, BLOKLEN+1, fout);
    buffer[0] = buffer[BLOKLEN];
    buffer[1] = buffer[BLOKLEN+1];
    buffer[2] = buffer[BLOKLEN+2];
    buffer[3] = buffer[BLOKLEN+3];
    pos -= BLOKLEN;
      }
      next = cc+2;
      cLength = (depth == 1)?3:depth+1;
    }
  }

  pos = AddCodeToBuffer(curNode->code, cLength, pos);
  if(pos-buffer>BLOKLEN-3) {
    buffer[-1] = BLOKLEN-3;
    fwrite(buffer-1, 1, BLOKLEN-2, fout);
    buffer[0] = buffer[BLOKLEN-3];
    buffer[1] = buffer[BLOKLEN-2];
    buffer[2] = buffer[BLOKLEN-1];
    buffer[3] = buffer[BLOKLEN];
    buffer[4] = buffer[BLOKLEN+1];
    pos -= BLOKLEN-3;
  }
  pos = AddCodeToBuffer(eoi, cLength, pos);
  pos = AddCodeToBuffer(0x0, -1, pos);
  buffer[-1] = pos-buffer;
  printf("5\n");

  fwrite(buffer-1, pos-buffer+1, 1, fout);
  //if((buffer -1) != NULL)
      //free(buffer-1);

  if((first->node) != NULL)
      free(first->node);
  if((baseNode) != NULL)
      free(baseNode);
  printf("6\n");
  printf("7\n");
  if (debugFlag) fprintf(stderr, "pixel count = %d; nodeCount = %d lookup
nodes = %d\n", tel, nodecount, lookuptypes);
  return 0;

}

void ClearTree(int cc, GifTree *root)
{
  int i;
  GifTree *newNode, **xx;

  if (debugFlag>1) fprintf(stderr, "Clear Tree  cc= %d\n", cc);
  if (debugFlag>1) fprintf(stderr, "nodeCount = %d lookup nodes = %d\n",
nodecount, lookuptypes);
  maxchainlen=0; lookuptypes = 1;
  nodecount = 0;
  nodeArray = root->node;
  xx= nodeArray;
  for (i = 0; i < noOfArrays; i++ ) {
    memmove (xx, empty, 256*sizeof(GifTree **));
    xx += 256;
  }
  topNode = baseNode;
  for(i=0; i<cc; i++) {
    root->node[i] = newNode = ++topNode;
    newNode->nxt = NULL;
    newNode->alt = NULL;
    newNode->code = i;
    newNode->ix = i;
    newNode->typ = TERMIN;
    newNode->node = empty;
    nodecount++;
  }
}

char *AddCodeToBuffer(int code, short n, char *buf)
{
  int    mask;

  if(n<0) {
    if(need<8) {
      buf++;
      *buf = 0x0;
    }
    need = 8;
    return buf;
  }

  while(n>=need) {
    mask = (1<<need)-1;
    *buf += (mask&code)<<(8-need);
    buf++;
    *buf = 0x0;
    code = code>>need;
    n -= need;
    need = 8;
  }
  if(n) {
    mask = (1<<n)-1;
    *buf += (mask&code)<<(8-need);
    need -= n;
  }
  return buf;
}


I hope this will be useful for someone :)

Best Regards.


Duponchelle Thibault



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]