///###////////////////////////////////////////////////////////////////////////
//
// Burton Computer Corporation
// http://www.burton-computer.com
// http://www.cooldevtools.com
// $Id: GifParser.cc 272 2007-01-06 19:37:27Z brian $
//
// Copyright (C) 2007 Burton Computer Corporation
// ALL RIGHTS RESERVED
//
// This program is open source software; you can redistribute it
// and/or modify it under the terms of the Q Public License (QPL)
// version 1.0. Use of this software in whole or in part, including
// linking it (modified or unmodified) into other programs is
// subject to the terms of the QPL.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// Q Public License for more details.
//
// You should have received a copy of the Q Public License
// along with this program; see the file LICENSE.txt.  If not, visit
// the Burton Computer Corporation or CoolDevTools web site
// QPL pages at:
//
//    http://www.burton-computer.com/qpl.html
//    http://www.cooldevtools.com/qpl.html
//

#if defined(HAVE_UNGIF)

#include <stdexcept>
#include "AbstractTokenizer.h"
#include "MD5Digester.h"
#include "Message.h"
#include "StringReader.h"
#include "GifParser.h"

static int throw_on_error(const char *function_name,
                          int rc)
{
  if (rc != GIF_ERROR) {
    return rc;
  }

  static string message(function_name);
  message += ": error: ";
  message += num_to_string(rc);
  throw runtime_error(message);
}

int GifParser::readFromBuffer(GifFileType *file,
                              GifByteType *dst,
                              int length)
{
  GifParser *parser = (GifParser *)file->UserData;
  int remaining = parser->m_bytes.length() - parser->m_nextByteIndex;
  if (remaining == 0) {
    return 0;
  }

  if (length > remaining) {
    length = remaining;
  }
  memcpy(dst, parser->m_bytes.get() + parser->m_nextByteIndex, length);
  parser->m_nextByteIndex += length;
  return length;
}

GifParser::GifParser(Message *message,
                     AbstractTokenizer *tokenizer,
                     AbstractTokenReceiver *receiver,
                     const string &prefix,
                     const Buffer<unsigned char> &bytes)
  : ImageParser(message, tokenizer, receiver, prefix, bytes),
    m_gif(0)
{
}

GifParser::~GifParser()
{
  if (m_gif) {
    DGifCloseFile(m_gif);
  }
}

bool GifParser::parseImage()
{
  try {
    openImage();
    digestImage();
    parseImageRecords();
  } catch (runtime_error &ex) {
    return false;
  }
}

void GifParser::openImage()
{
  m_nextByteIndex = 0;
  m_gif = DGifOpen(this, readFromBuffer);
  if (!m_gif) {
    throw runtime_error("open gif failed");
  }
}

void GifParser::parseImageRecords()
{
  GifRecordType rec_type;

  int max_loops = 1500;
  int image_num = 0;
  while ((max_loops-- > 0) && (throw_on_error("DGifGetRecordType", DGifGetRecordType(m_gif, &rec_type)) != TERMINATE_RECORD_TYPE)) {
    string base_token("image_");
    base_token += num_to_string(image_num);
    switch (rec_type) {
    case IMAGE_DESC_RECORD_TYPE:
      {
        throw_on_error("DGifGetImageDesc", DGifGetImageDesc(m_gif));
        sendToken(base_token + "_height_" + num_to_string(m_gif->Image.Height));
        sendToken(base_token + "_width_" + num_to_string(m_gif->Image.Width));
        sendToken(base_token + "_left_" + num_to_string(m_gif->Image.Left));
        sendToken(base_token + "_top_" + num_to_string(m_gif->Image.Top));
        sendToken(base_token + (m_gif->Image.Interlace ? "_interlaced" : "_noninterlaced"));
        sendToken(base_token + (m_gif->Image.ColorMap ? "_color_map" : "_no_color_map"));
        if (m_gif->Image.ColorMap) {
          sendToken(base_token + "_bpp_" + num_to_string(m_gif->Image.ColorMap->BitsPerPixel));
          int len = 1 << m_gif->Image.ColorMap->BitsPerPixel;
          for (int i = 0; i < len; i+= 4) {
            for (int j = 0; j < 4 && j < len; ++j) {
              sendToken(base_token + "_color_red_" + num_to_string(m_gif->Image.ColorMap->Colors[i + j].Red));
              sendToken(base_token + "_color_green_" + num_to_string(m_gif->Image.ColorMap->Colors[i + j].Green));
              sendToken(base_token + "_color_blue_" + num_to_string(m_gif->Image.ColorMap->Colors[i + j].Blue));
            }
          }
        }

        // skip the image
        int code_size;
        GifByteType *code_block;
        throw_on_error("DGifGetCode", DGifGetCode(m_gif, &code_size, &code_block));
        while (code_block) {
          throw_on_error("DGifGetCodeNext", DGifGetCodeNext(m_gif, &code_block));
        }

        ++image_num;
      }
      break;

    case EXTENSION_RECORD_TYPE:
      {
        int ext_code;
        GifByteType *extension;
        throw_on_error("DGifGetExtension", DGifGetExtension(m_gif, &ext_code, &extension));
        sendToken(base_token + "_extcode_" + num_to_string(ext_code));
        while (extension) {
          const int slen = (int)extension[0];
          string prefix(base_token);
          prefix += "_extchars_";
          string token;
          for (int i = 1; i <= slen; ++i) {
            if (isprint(extension[i])) {
              token += extension[i];
            } else {
              token += '{';
              token += num_to_string(extension[i]);
              token += '}';
            }
          }
          sendToken(prefix + token);
          StringReader reader(token);
          m_tokenizer->tokenize(m_receiver, &reader, m_prefix + prefix);
          throw_on_error("DGifGetExtensionNext", DGifGetExtensionNext(m_gif, &extension));
        }
      }
      break;

    default:
      break;
    }
  }
}

#endif // HAVE_UNGIF


syntax highlighted by Code2HTML, v. 0.9.1