text: /*
   * This file is part of libbluray
   * Copyright (C) 2010  Petri Hintukainen <phintuka@users.sourceforge.net>
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library 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 GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library. If not, see
   * <http://www.gnu.org/licenses/>.
   */
  
  #if HAVE_CONFIG_H
  #include "config.h"
  #endif
  
  #include "pg_decode.h"
  
  #include "util/refcnt.h"
  #include "util/macro.h"
  #include "util/logging.h"
  #include "util/bits.h"
  
  #include <string.h>
  #include <stdlib.h>
  
  /*
   *
   */
  
  void pg_decode_video_descriptor(BITBUFFER *bb, BD_PG_VIDEO_DESCRIPTOR *p)
  {
      p->video_width  = bb_read(bb, 16);
      p->video_height = bb_read(bb, 16);
      p->frame_rate   = bb_read(bb, 4);
      bb_skip(bb, 4);
  }
  
  void pg_decode_composition_descriptor(BITBUFFER *bb, BD_PG_COMPOSITION_DESCRIPTOR *p)
  {
      p->number = bb_read(bb, 16);
      p->state  = bb_read(bb, 2);
      bb_skip(bb, 6);
  }
  
  void pg_decode_sequence_descriptor(BITBUFFER *bb, BD_PG_SEQUENCE_DESCRIPTOR *p)
  {
      p->first_in_seq = bb_read(bb, 1);
      p->last_in_seq  = bb_read(bb, 1);
      bb_skip(bb, 6);
  }
  
  void pg_decode_window(BITBUFFER *bb, BD_PG_WINDOW *p)
  {
      p->id     = bb_read(bb, 8);
      p->x      = bb_read(bb, 16);
      p->y      = bb_read(bb, 16);
      p->width  = bb_read(bb, 16);
      p->height = bb_read(bb, 16);
  }
  
  void pg_decode_composition_object(BITBUFFER *bb, BD_PG_COMPOSITION_OBJECT *p)
  {
      p->object_id_ref = bb_read(bb, 16);
      p->window_id_ref = bb_read(bb, 8);
  
      p->crop_flag      = bb_read(bb, 1);
      p->forced_on_flag = bb_read(bb, 1);
      bb_skip(bb, 6);
  
      p->x = bb_read(bb, 16);
      p->y = bb_read(bb, 16);
  
      if (p->crop_flag) {
          p->crop_x = bb_read(bb, 16);
          p->crop_y = bb_read(bb, 16);
          p->crop_w = bb_read(bb, 16);
          p->crop_h = bb_read(bb, 16);
      }
  }
  
  void pg_decode_palette_entry(BITBUFFER *bb, BD_PG_PALETTE_ENTRY *entry)
  {
      uint8_t entry_id = bb_read(bb, 8);
  
      entry[entry_id].Y  = bb_read(bb, 8);
      entry[entry_id].Cr = bb_read(bb, 8);
      entry[entry_id].Cb = bb_read(bb, 8);
      entry[entry_id].T  = bb_read(bb, 8);
  }
  
  /*
   * segments
   */
  
  int pg_decode_palette_update(BITBUFFER *bb, BD_PG_PALETTE *p)
  {
      p->id      = bb_read(bb, 8);
      p->version = bb_read(bb, 8);
  
      while (!bb_eof(bb)) {
          pg_decode_palette_entry(bb, p->entry);
      }
  
      return 1;
  }
  
  int pg_decode_palette(BITBUFFER *bb, BD_PG_PALETTE *p)
  {
      memset(p->entry, 0, sizeof(p->entry));
  
      return pg_decode_palette_update(bb, p);
  }
  
  static int _decode_rle(BITBUFFER *bb, BD_PG_OBJECT *p)
  {
      BD_PG_RLE_ELEM *tmp;
      int pixels_left = p->width * p->height;
      int num_rle     = 0;
      int rle_size    = p->width * p->height / 4;
  
      if (rle_size < 1)
          rle_size = 1;
  
      tmp = refcnt_realloc(p->img, rle_size * sizeof(BD_PG_RLE_ELEM));
      if (!tmp) {
          BD_DEBUG(DBG_DECODE | DBG_CRIT, "pg_decode_object(): realloc failed\n");
          return 0;
      }
      p->img = tmp;
  
      while (!bb_eof(bb)) {
          uint32_t len   = 1;
          uint8_t  color = 0;
  
          if (!(color = bb_read(bb, 8))) {
              if (!bb_read(bb, 1)) {
                  if (!bb_read(bb, 1)) {
                      len = bb_read(bb, 6);
                  } else {
                      len = bb_read(bb, 14);
                  }
              } else {
                  if (!bb_read(bb, 1)) {
                      len = bb_read(bb, 6);
                  } else {
                      len = bb_read(bb, 14);
                  }
                  color = bb_read(bb, 8);
              }
          }
  
          p->img[num_rle].len   = len;
          p->img[num_rle].color = color;
  
          pixels_left -= len;
  
          if (pixels_left < 0) {
              BD_DEBUG(DBG_DECODE, "pg_decode_object(): too many pixels (%d)\n", -pixels_left);
              return 0;
          }
  
          num_rle++;
          if (num_rle >= rle_size) {
              rle_size *= 2;
              tmp = refcnt_realloc(p->img, rle_size * sizeof(BD_PG_RLE_ELEM));
              if (!tmp) {
                  BD_DEBUG(DBG_DECODE | DBG_CRIT, "pg_decode_object(): realloc failed\n");
                  return 0;
              }
              p->img = tmp;
          }
      }
  
      if (pixels_left > 0) {
          BD_DEBUG(DBG_DECODE, "pg_decode_object(): missing %d pixels\n", pixels_left);
          return 0;
      }
  
      return 1;
  }
  
  int pg_decode_object(BITBUFFER *bb, BD_PG_OBJECT *p)
  {
      BD_PG_SEQUENCE_DESCRIPTOR sd;
  
      p->id      = bb_read(bb, 16);
      p->version = bb_read(bb, 8);
  
      pg_decode_sequence_descriptor(bb, &sd);
  
      /* splitted segments should be already joined */
      if (!sd.first_in_seq) {
          BD_DEBUG(DBG_DECODE, "pg_decode_object(): not first in sequence\n");
          return 0;
      }
      if (!sd.last_in_seq) {
          BD_DEBUG(DBG_DECODE, "pg_decode_object(): not last in sequence\n");
          return 0;
      }
  
      if (!bb_is_align(bb, 0x07)) {
        BD_DEBUG(DBG_DECODE, "pg_decode_object(): alignment error\n");
        return 0;
      }
  
      uint32_t data_len = bb_read(bb, 24);
      uint32_t buf_len  = bb->p_end - bb->p;
      if (data_len != buf_len) {
          BD_DEBUG(DBG_DECODE, "pg_decode_object(): buffer size mismatch (expected %d, have %d)\n", data_len, buf_len);
          return 0;
      }
  
      p->width  = bb_read(bb, 16);
      p->height = bb_read(bb, 16);
  
      return _decode_rle(bb, p);
  }
  
  int pg_decode_composition(BITBUFFER *bb, BD_PG_COMPOSITION *p)
  {
      unsigned ii;
  
      pg_decode_video_descriptor(bb, &p->video_descriptor);
      pg_decode_composition_descriptor(bb, &p->composition_descriptor);
  
      p->palette_update_flag = bb_read(bb, 1);
      bb_skip(bb, 7);
  
      p->palette_id_ref      = bb_read(bb, 8);
  
      p->num_composition_objects = bb_read(bb, 8);
      p->composition_object      = calloc(p->num_composition_objects, sizeof(BD_PG_COMPOSITION_OBJECT));
      if (!p->composition_object) {
          BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
          return 0;
      }
  
      for (ii = 0; ii < p->num_composition_objects; ii++) {
          pg_decode_composition_object(bb, &p->composition_object[ii]);
      }
  
      return 1;
  }
  
  int pg_decode_windows(BITBUFFER *bb, BD_PG_WINDOWS *p)
  {
      unsigned ii;
  
      p->num_windows = bb_read(bb, 8);
      p->window = calloc(p->num_windows, sizeof(BD_PG_WINDOW));
      if (!p->window) {
          BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
          return 0;
      }
  
      for (ii = 0; ii < p->num_windows; ii++) {
          pg_decode_window(bb, &p->window[ii]);
      }
  
      return 1;
  }
  
  /*
   * cleanup
   */
  
  void pg_clean_object(BD_PG_OBJECT *p)
  {
      if (p) {
          bd_refcnt_dec(p->img);
          p->img = NULL;
      }
  }
  
  void pg_clean_windows(BD_PG_WINDOWS *p)
  {
      if (p) {
          X_FREE(p->window);
      }
  }
  
  static void pg_clean_composition(BD_PG_COMPOSITION *p)
  {
      if (p) {
          X_FREE(p->composition_object);
      }
  }
  
  void pg_free_composition(BD_PG_COMPOSITION **p)
  {
      if (p && *p) {
          pg_clean_composition(*p);
          X_FREE(*p);
      }
  }
  