/*
* 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);
}
}