// Copyright 2011 Google Inc.
//
// This code is licensed under the same terms as WebM:
//  Software License Agreement:  http://www.webmproject.org/license/software/
//  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
// -----------------------------------------------------------------------------
//
// Alpha-plane compression.
//
// Author: Skal (pascal.massimino@gmail.com)

#include <assert.h>
#include <stdlib.h>
#include "vp8enci.h"

#ifdef WEBP_EXPERIMENTAL_FEATURES
#include "zlib.h"
#endif

#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

#ifdef WEBP_EXPERIMENTAL_FEATURES

#define CHUNK_SIZE 8192

//-----------------------------------------------------------------------------

static int CompressAlpha(const uint8_t* data, size_t data_size,
                         uint8_t** output, size_t* output_size,
                         int algo) {
  int ret = Z_OK;
  z_stream strm;
  unsigned char chunk[CHUNK_SIZE];

  *output = NULL;
  *output_size = 0;
  memset(&strm, 0, sizeof(strm));
  if (deflateInit(&strm, algo ? Z_BEST_SPEED : Z_BEST_COMPRESSION) != Z_OK) {
    return 0;
  }
  strm.next_in = (unsigned char*)data;
  strm.avail_in = data_size;
  do {
    size_t size_out;

    strm.next_out = chunk;
    strm.avail_out = CHUNK_SIZE;
    ret = deflate(&strm, Z_FINISH);
    if (ret == Z_STREAM_ERROR) {
      break;
    }
    size_out = CHUNK_SIZE - strm.avail_out;
    if (size_out) {
      size_t new_size = *output_size + size_out;
      uint8_t* new_output = realloc(*output, new_size);
      if (new_output == NULL) {
        ret = Z_MEM_ERROR;
        break;
      }
      memcpy(new_output + *output_size, chunk, size_out);
      *output_size = new_size;
      *output = new_output;
    }
  } while (ret != Z_STREAM_END || strm.avail_out == 0);

  deflateEnd(&strm);
  if (ret != Z_STREAM_END) {
    free(*output);
    output_size = 0;
    return 0;
  }
  return 1;
}

#endif    /* WEBP_EXPERIMENTAL_FEATURES */

void VP8EncInitAlpha(VP8Encoder* enc) {
  enc->has_alpha_ = (enc->pic_->a != NULL);
  enc->alpha_data_ = NULL;
  enc->alpha_data_size_ = 0;
}

void VP8EncCodeAlphaBlock(VP8EncIterator* it) {
  (void)it;
  // Nothing for now. We just ZLIB-compress in the end.
}

int VP8EncFinishAlpha(VP8Encoder* enc) {
  if (enc->has_alpha_) {
#ifdef WEBP_EXPERIMENTAL_FEATURES
    const WebPPicture* pic = enc->pic_;
    assert(pic->a);
    if (!CompressAlpha(pic->a, pic->width * pic->height,
                       &enc->alpha_data_, &enc->alpha_data_size_,
                       enc->config_->alpha_compression)) {
      return 0;
    }
#endif
  }
  return 1;
}

void VP8EncDeleteAlpha(VP8Encoder* enc) {
  free(enc->alpha_data_);
  enc->alpha_data_ = NULL;
  enc->alpha_data_size_ = 0;
  enc->has_alpha_ = 0;
}

#if defined(__cplusplus) || defined(c_plusplus)
}    // extern "C"
#endif