forked from mirror/libbpg
1560 lines
42 KiB
C
1560 lines
42 KiB
C
/*
|
|
* libbpg
|
|
*
|
|
* Copyright (c) 2014 Fabrice Bellard
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include <math.h>
|
|
#ifdef EMSCRIPTEN
|
|
#include <emscripten.h>
|
|
#endif
|
|
|
|
#include <libavutil/opt.h>
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavutil/common.h>
|
|
|
|
#ifndef EMSCRIPTEN
|
|
#define USE_RGB48
|
|
//#define DEBUG
|
|
#endif
|
|
|
|
#if !defined(DEBUG)
|
|
#define NDEBUG
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include "libbpg.h"
|
|
|
|
#define BPG_HEADER_MAGIC 0x425047fb
|
|
|
|
#define ITAPS2 4
|
|
#define ITAPS (2 * ITAPS2) /* number of taps of the interpolation filter */
|
|
|
|
#ifdef USE_VAR_BIT_DEPTH
|
|
typedef uint16_t PIXEL;
|
|
#else
|
|
typedef uint8_t PIXEL;
|
|
#endif
|
|
|
|
#define MAX_DATA_SIZE ((1 << 30) - 1)
|
|
|
|
typedef struct {
|
|
int c_shift;
|
|
int c_rnd;
|
|
int c_one;
|
|
int y_one, y_offset;
|
|
int c_r_cr, c_g_cb, c_g_cr, c_b_cb;
|
|
int c_center;
|
|
int bit_depth;
|
|
int limited_range;
|
|
} ColorConvertState;
|
|
|
|
typedef void ColorConvertFunc(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr);
|
|
|
|
struct BPGDecoderContext {
|
|
AVFrame *frame;
|
|
AVFrame *alpha_frame;
|
|
int w, h;
|
|
BPGImageFormatEnum format;
|
|
uint8_t has_alpha; /* true if alpha or W plane */
|
|
uint8_t bit_depth;
|
|
uint8_t has_w_plane;
|
|
uint8_t limited_range;
|
|
uint8_t premultiplied_alpha;
|
|
BPGColorSpaceEnum color_space;
|
|
int keep_extension_data; /* true if the extension data must be
|
|
kept during parsing */
|
|
BPGExtensionData *first_md;
|
|
|
|
/* the following is used for format conversion */
|
|
uint8_t is_rgba;
|
|
uint8_t is_rgb48;
|
|
int y; /* current line */
|
|
int w2, h2;
|
|
const uint8_t *y_buf, *cb_buf, *cr_buf, *a_buf;
|
|
int y_linesize, cb_linesize, cr_linesize, a_linesize;
|
|
PIXEL *cb_buf2, *cr_buf2, *cb_buf3[ITAPS], *cr_buf3[ITAPS];
|
|
int16_t *c_buf4;
|
|
ColorConvertState cvt;
|
|
ColorConvertFunc *cvt_func;
|
|
};
|
|
|
|
/* ffmpeg utilities */
|
|
#ifdef USE_AV_LOG
|
|
void av_log(void* avcl, int level, const char *fmt, ...)
|
|
{
|
|
#ifdef DEBUG
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
#endif
|
|
}
|
|
|
|
void avpriv_report_missing_feature(void *avc, const char *msg, ...)
|
|
{
|
|
#ifdef DEBUG
|
|
va_list ap;
|
|
|
|
va_start(ap, msg);
|
|
vfprintf(stderr, msg, ap);
|
|
va_end(ap);
|
|
#endif
|
|
}
|
|
#endif /* USE_AV_LOG */
|
|
|
|
/* return < 0 if error, otherwise the consumed length */
|
|
static int get_ue32(uint32_t *pv, const uint8_t *buf, int len)
|
|
{
|
|
const uint8_t *p;
|
|
uint32_t v;
|
|
int a;
|
|
|
|
if (len <= 0)
|
|
return -1;
|
|
p = buf;
|
|
a = *p++;
|
|
len--;
|
|
if (a < 0x80) {
|
|
*pv = a;
|
|
return 1;
|
|
} else if (a == 0x80) {
|
|
/* we don't accept non canonical encodings */
|
|
return -1;
|
|
}
|
|
v = a & 0x7f;
|
|
for(;;) {
|
|
if (len <= 0)
|
|
return -1;
|
|
a = *p++;
|
|
len--;
|
|
v = (v << 7) | (a & 0x7f);
|
|
if (!(a & 0x80))
|
|
break;
|
|
}
|
|
*pv = v;
|
|
return p - buf;
|
|
}
|
|
|
|
static int get_ue(uint32_t *pv, const uint8_t *buf, int len)
|
|
{
|
|
int ret;
|
|
ret = get_ue32(pv, buf, len);
|
|
if (ret < 0)
|
|
return ret;
|
|
/* limit the maximum size to avoid overflows in buffer
|
|
computations */
|
|
if (*pv > MAX_DATA_SIZE)
|
|
return -1;
|
|
return ret;
|
|
}
|
|
|
|
static int decode_write_frame(AVCodecContext *avctx,
|
|
AVFrame *frame, int *frame_count, AVPacket *pkt, int last)
|
|
{
|
|
int len, got_frame;
|
|
|
|
len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);
|
|
if (len < 0) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Error while decoding frame %d\n", *frame_count);
|
|
#endif
|
|
return len;
|
|
}
|
|
if (got_frame) {
|
|
#ifdef DEBUG
|
|
printf("Saving %sframe %3d\n", last ? "last " : "", *frame_count);
|
|
#endif
|
|
(*frame_count)++;
|
|
}
|
|
if (pkt->data) {
|
|
pkt->size -= len;
|
|
pkt->data += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern AVCodec ff_hevc_decoder;
|
|
|
|
static AVFrame *hevc_decode(const uint8_t *input_data, int input_data_len,
|
|
int width, int height, int chroma_format_idc,
|
|
int bit_depth)
|
|
{
|
|
AVCodec *codec;
|
|
AVCodecContext *c= NULL;
|
|
int frame_count, idx, msps_len, ret, buf_len, i;
|
|
AVPacket avpkt;
|
|
AVFrame *frame;
|
|
uint32_t len;
|
|
uint8_t *buf, *msps_buf;
|
|
|
|
/* build the modified SPS header to please libavcodec */
|
|
ret = get_ue(&len, input_data, input_data_len);
|
|
if (ret < 0)
|
|
return NULL;
|
|
input_data += ret;
|
|
input_data_len -= ret;
|
|
|
|
if (len > input_data_len)
|
|
return NULL;
|
|
|
|
msps_len = 1 + 4 + 4 + 1 + len;
|
|
msps_buf = av_malloc(msps_len);
|
|
idx = 0;
|
|
msps_buf[idx++] = chroma_format_idc;
|
|
msps_buf[idx++] = (width >> 24);
|
|
msps_buf[idx++] = (width >> 16);
|
|
msps_buf[idx++] = (width >> 8);
|
|
msps_buf[idx++] = (width >> 0);
|
|
msps_buf[idx++] = (height >> 24);
|
|
msps_buf[idx++] = (height >> 16);
|
|
msps_buf[idx++] = (height >> 8);
|
|
msps_buf[idx++] = (height >> 0);
|
|
msps_buf[idx++] = bit_depth - 8;
|
|
memcpy(msps_buf + idx, input_data, len);
|
|
idx += len;
|
|
assert(idx == msps_len);
|
|
input_data += len;
|
|
input_data_len -= len;
|
|
|
|
buf_len = 4 + 2 + msps_len * 2 + 4 + (input_data_len - len);
|
|
buf = av_malloc(buf_len);
|
|
|
|
idx = 0;
|
|
/* NAL header */
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x01;
|
|
buf[idx++] = (48 << 1); /* application specific NAL unit type */
|
|
buf[idx++] = 1;
|
|
|
|
/* add the modified SPS with the correct escape codes */
|
|
i = 0;
|
|
while (i < msps_len) {
|
|
if ((i + 1) < msps_len && msps_buf[i] == 0 && msps_buf[i + 1] == 0) {
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x03;
|
|
i += 2;
|
|
} else {
|
|
buf[idx++] = msps_buf[i++];
|
|
}
|
|
}
|
|
/* the last byte cannot be 0 */
|
|
if (idx == 0 || buf[idx - 1] == 0x00)
|
|
buf[idx++] = 0x80;
|
|
|
|
av_free(msps_buf);
|
|
|
|
/* NAL start code (Note: should be 3 bytes depending on exact NAL
|
|
type, but it is not critical for libavcodec) */
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x00;
|
|
buf[idx++] = 0x01;
|
|
|
|
memcpy(buf + idx, input_data, input_data_len);
|
|
idx += input_data_len;
|
|
|
|
assert(idx < buf_len);
|
|
|
|
av_init_packet(&avpkt);
|
|
|
|
codec = &ff_hevc_decoder;
|
|
|
|
c = avcodec_alloc_context3(codec);
|
|
if (!c) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Could not allocate video codec context\n");
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
if(codec->capabilities&CODEC_CAP_TRUNCATED)
|
|
c->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
|
|
|
|
/* for testing: use the MD5 or CRC in SEI to check the decoded bit
|
|
stream. */
|
|
c->err_recognition |= AV_EF_CRCCHECK;
|
|
|
|
/* open it */
|
|
if (avcodec_open2(c, codec, NULL) < 0) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Could not open codec\n");
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
frame = av_frame_alloc();
|
|
if (!frame) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Could not allocate video frame\n");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
avpkt.size = idx;
|
|
avpkt.data = buf;
|
|
frame_count = 0;
|
|
while (avpkt.size > 0) {
|
|
if (decode_write_frame(c, frame, &frame_count, &avpkt, 0) < 0)
|
|
exit(1);
|
|
}
|
|
|
|
avcodec_close(c);
|
|
av_free(c);
|
|
av_free(buf);
|
|
|
|
if (frame_count == 0) {
|
|
av_frame_free(&frame);
|
|
return NULL;
|
|
} else {
|
|
return frame;
|
|
}
|
|
}
|
|
|
|
uint8_t *bpg_decoder_get_data(BPGDecoderContext *img, int *pline_size, int plane)
|
|
{
|
|
int c_count;
|
|
if (img->format == BPG_FORMAT_GRAY)
|
|
c_count = 1;
|
|
else
|
|
c_count = 3;
|
|
if (plane < c_count) {
|
|
*pline_size = img->frame->linesize[plane];
|
|
return img->frame->data[plane];
|
|
} else if (img->has_alpha && plane == c_count) {
|
|
*pline_size = img->alpha_frame->linesize[0];
|
|
return img->alpha_frame->data[0];
|
|
} else {
|
|
*pline_size = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int bpg_decoder_get_info(BPGDecoderContext *img, BPGImageInfo *p)
|
|
{
|
|
if (!img->frame)
|
|
return -1;
|
|
p->width = img->w;
|
|
p->height = img->h;
|
|
p->format = img->format;
|
|
p->has_alpha = img->has_alpha && !img->has_w_plane;
|
|
p->premultiplied_alpha = img->premultiplied_alpha;
|
|
p->has_w_plane = img->has_w_plane;
|
|
p->limited_range = img->limited_range;
|
|
p->color_space = img->color_space;
|
|
p->bit_depth = img->bit_depth;
|
|
return 0;
|
|
}
|
|
|
|
static inline int clamp_pix(int a, int pixel_max)
|
|
{
|
|
if (a < 0)
|
|
return 0;
|
|
else if (a > pixel_max)
|
|
return pixel_max;
|
|
else
|
|
return a;
|
|
}
|
|
|
|
static inline int clamp8(int a)
|
|
{
|
|
if (a < 0)
|
|
return 0;
|
|
else if (a > 255)
|
|
return 255;
|
|
else
|
|
return a;
|
|
}
|
|
|
|
/* 7 tap Lanczos interpolator */
|
|
#define IC0 (-1)
|
|
#define IC1 4
|
|
#define IC2 (-10)
|
|
#define IC3 57
|
|
#define IC4 18
|
|
#define IC5 (-6)
|
|
#define IC6 2
|
|
|
|
/* interpolate by a factor of two assuming chroma is between the luma
|
|
samples. */
|
|
static void interp2_simple(PIXEL *dst, const PIXEL *src, int n, int bit_depth)
|
|
{
|
|
int pixel_max, a0, a1, a2, a3, a4, a5, a6;
|
|
|
|
pixel_max = (1 << bit_depth) - 1;
|
|
|
|
a1 = src[-3];
|
|
a2 = src[-2];
|
|
a3 = src[-1];
|
|
a4 = src[0];
|
|
a5 = src[1];
|
|
a6 = src[2];
|
|
|
|
while (n >= 2) {
|
|
a0 = a1;
|
|
a1 = a2;
|
|
a2 = a3;
|
|
a3 = a4;
|
|
a4 = a5;
|
|
a5 = a6;
|
|
a6 = src[3];
|
|
dst[0] = clamp_pix((a0 * IC6 + a1 * IC5 + a2 * IC4 + a3 * IC3 +
|
|
a4 * IC2 + a5 * IC1 + a6 * IC0 + 32) >> 6,
|
|
pixel_max);
|
|
dst[1] = clamp_pix((a0 * IC0 + a1 * IC1 + a2 * IC2 + a3 * IC3 +
|
|
a4 * IC4 + a5 * IC5 + a6 * IC6 + 32) >> 6,
|
|
pixel_max);
|
|
dst += 2;
|
|
src++;
|
|
n -= 2;
|
|
}
|
|
if (n) {
|
|
a0 = a1;
|
|
a1 = a2;
|
|
a2 = a3;
|
|
a3 = a4;
|
|
a4 = a5;
|
|
a5 = a6;
|
|
a6 = src[3];
|
|
dst[0] = clamp_pix((a0 * IC6 + a1 * IC5 + a2 * IC4 + a3 * IC3 +
|
|
a4 * IC2 + a5 * IC1 + a6 * IC0 + 32) >> 6,
|
|
pixel_max);
|
|
}
|
|
}
|
|
|
|
static void interp2_h(PIXEL *dst, const PIXEL *src, int n, int bit_depth)
|
|
{
|
|
PIXEL *src1, v;
|
|
int i, n2;
|
|
|
|
/* add extra pixels and do the interpolation (XXX: could go faster) */
|
|
n2 = (n + 1) / 2;
|
|
src1 = av_malloc((n2 + ITAPS - 1) * sizeof(PIXEL));
|
|
memcpy(src1 + ITAPS2 - 1, src, n2 * sizeof(PIXEL));
|
|
|
|
v = src[0];
|
|
for(i = 0; i < ITAPS2 - 1; i++)
|
|
src1[i] = v;
|
|
|
|
v = src[n2 - 1];
|
|
for(i = 0; i < ITAPS2; i++)
|
|
src1[ITAPS2 - 1 + n2 + i] = v;
|
|
interp2_simple(dst, src1 + ITAPS2 - 1, n, bit_depth);
|
|
av_free(src1);
|
|
}
|
|
|
|
static void interp2_simple2(PIXEL *dst, const int16_t *src, int n,
|
|
int bit_depth)
|
|
{
|
|
int shift, offset, pixel_max, a0, a1, a2, a3, a4, a5, a6;
|
|
|
|
pixel_max = (1 << bit_depth) - 1;
|
|
shift = 20 - bit_depth;
|
|
offset = 1 << (shift - 1);
|
|
|
|
a1 = src[-3];
|
|
a2 = src[-2];
|
|
a3 = src[-1];
|
|
a4 = src[0];
|
|
a5 = src[1];
|
|
a6 = src[2];
|
|
|
|
while (n >= 2) {
|
|
a0 = a1;
|
|
a1 = a2;
|
|
a2 = a3;
|
|
a3 = a4;
|
|
a4 = a5;
|
|
a5 = a6;
|
|
a6 = src[3];
|
|
dst[0] = clamp_pix((a0 * IC6 + a1 * IC5 + a2 * IC4 + a3 * IC3 +
|
|
a4 * IC2 + a5 * IC1 + a6 * IC0 + offset) >> shift,
|
|
pixel_max);
|
|
dst[1] = clamp_pix((a0 * IC0 + a1 * IC1 + a2 * IC2 + a3 * IC3 +
|
|
a4 * IC4 + a5 * IC5 + a6 * IC6 + offset) >> shift,
|
|
pixel_max);
|
|
dst += 2;
|
|
src++;
|
|
n -= 2;
|
|
}
|
|
if (n) {
|
|
a0 = a1;
|
|
a1 = a2;
|
|
a2 = a3;
|
|
a3 = a4;
|
|
a4 = a5;
|
|
a5 = a6;
|
|
a6 = src[3];
|
|
dst[0] = clamp_pix((a0 * IC6 + a1 * IC5 + a2 * IC4 + a3 * IC3 +
|
|
a4 * IC2 + a5 * IC1 + a6 * IC0 + offset) >> shift,
|
|
pixel_max);
|
|
}
|
|
}
|
|
|
|
/* y_pos is the position of the sample '0' in the 'src' circular
|
|
buffer. tmp is a temporary buffer of length (n2 + 2 * ITAPS - 1) */
|
|
static void interp2_vh(PIXEL *dst, PIXEL **src, int n, int y_pos,
|
|
int16_t *tmp_buf, int bit_depth, int frac_pos)
|
|
{
|
|
const PIXEL *src0, *src1, *src2, *src3, *src4, *src5, *src6;
|
|
int i, n2, shift;
|
|
PIXEL v;
|
|
|
|
src0 = src[(y_pos - 3) & 7];
|
|
src1 = src[(y_pos - 2) & 7];
|
|
src2 = src[(y_pos - 1) & 7];
|
|
src3 = src[(y_pos + 0) & 7];
|
|
src4 = src[(y_pos + 1) & 7];
|
|
src5 = src[(y_pos + 2) & 7];
|
|
src6 = src[(y_pos + 3) & 7];
|
|
|
|
/* vertical interpolation first */
|
|
/* XXX: should round but not critical */
|
|
shift = bit_depth - 8;
|
|
n2 = (n + 1) / 2;
|
|
if (frac_pos == 0) {
|
|
for(i = 0; i < n2; i++) {
|
|
tmp_buf[ITAPS2 - 1 + i] =
|
|
(src0[i] * IC6 + src1[i] * IC5 +
|
|
src2[i] * IC4 + src3[i] * IC3 +
|
|
src4[i] * IC2 + src5[i] * IC1 +
|
|
src6[i] * IC0) >> shift;
|
|
}
|
|
} else {
|
|
for(i = 0; i < n2; i++) {
|
|
tmp_buf[ITAPS2 - 1 + i] =
|
|
(src0[i] * IC0 + src1[i] * IC1 +
|
|
src2[i] * IC2 + src3[i] * IC3 +
|
|
src4[i] * IC4 + src5[i] * IC5 +
|
|
src6[i] * IC6) >> shift;
|
|
}
|
|
}
|
|
|
|
/* then horizontal interpolation */
|
|
v = tmp_buf[ITAPS2 - 1];
|
|
for(i = 0; i < ITAPS2 - 1; i++)
|
|
tmp_buf[i] = v;
|
|
v = tmp_buf[ITAPS2 - 1 + n2 - 1];
|
|
for(i = 0; i < ITAPS2; i++)
|
|
tmp_buf[ITAPS2 - 1 + n2 + i] = v;
|
|
interp2_simple2(dst, tmp_buf + ITAPS2 - 1, n, bit_depth);
|
|
}
|
|
|
|
static void ycc_to_rgb24(ColorConvertState *s, uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int y_val, cb_val, cr_val, x;
|
|
int c_r_cr, c_g_cb, c_g_cr, c_b_cb, rnd, shift, center, c_one;
|
|
|
|
c_r_cr = s->c_r_cr;
|
|
c_g_cb = s->c_g_cb;
|
|
c_g_cr = s->c_g_cr;
|
|
c_b_cb = s->c_b_cb;
|
|
c_one = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
center = s->c_center;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x] * c_one;
|
|
cb_val = cb_ptr[x] - center;
|
|
cr_val = cr_ptr[x] - center;
|
|
q[0] = clamp8((y_val + c_r_cr * cr_val + rnd) >> shift);
|
|
q[1] = clamp8((y_val - c_g_cb * cb_val - c_g_cr * cr_val + rnd) >> shift);
|
|
q[2] = clamp8((y_val + c_b_cb * cb_val + rnd) >> shift);
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void ycgco_to_rgb24(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int y_val, cb_val, cr_val, x;
|
|
int rnd, shift, center, c_one;
|
|
|
|
c_one = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
center = s->c_center;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x];
|
|
cb_val = cb_ptr[x] - center;
|
|
cr_val = cr_ptr[x] - center;
|
|
q[0] = clamp8(((y_val - cb_val + cr_val) * c_one + rnd) >> shift);
|
|
q[1] = clamp8(((y_val + cb_val) * c_one + rnd) >> shift);
|
|
q[2] = clamp8(((y_val - cb_val - cr_val) * c_one + rnd) >> shift);
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
/* c = c * alpha */
|
|
static void alpha_combine8(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *a_ptr, int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int x, a_val, shift, rnd;
|
|
|
|
shift = s->bit_depth;
|
|
rnd = 1 << (shift - 1);
|
|
for(x = 0; x < n; x++) {
|
|
a_val = a_ptr[x];
|
|
/* XXX: not accurate enough */
|
|
q[0] = (q[0] * a_val + rnd) >> shift;
|
|
q[1] = (q[1] * a_val + rnd) >> shift;
|
|
q[2] = (q[2] * a_val + rnd) >> shift;
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static uint32_t divide8_table[256];
|
|
|
|
#define DIV8_BITS 16
|
|
|
|
static void alpha_divide8_init(void)
|
|
{
|
|
int i;
|
|
for(i = 1; i < 256; i++) {
|
|
/* Note: the 128 is added to have 100% correct results for all
|
|
the values */
|
|
divide8_table[i] = ((255 << DIV8_BITS) + (i / 2) + 128) / i;
|
|
}
|
|
}
|
|
|
|
static inline unsigned int comp_divide8(unsigned int val, unsigned int alpha,
|
|
unsigned int alpha_inv)
|
|
{
|
|
if (val >= alpha)
|
|
return 255;
|
|
return (val * alpha_inv + (1 << (DIV8_BITS - 1))) >> DIV8_BITS;
|
|
}
|
|
|
|
/* c = c / alpha */
|
|
static void alpha_divide8(uint8_t *dst, int n)
|
|
{
|
|
static int inited;
|
|
uint8_t *q = dst;
|
|
int x;
|
|
unsigned int a_val, a_inv;
|
|
|
|
if (!inited) {
|
|
inited = 1;
|
|
alpha_divide8_init();
|
|
}
|
|
|
|
for(x = 0; x < n; x++) {
|
|
a_val = q[3];
|
|
if (a_val == 0) {
|
|
q[0] = 255;
|
|
q[1] = 255;
|
|
q[2] = 255;
|
|
} else {
|
|
a_inv = divide8_table[a_val];
|
|
q[0] = comp_divide8(q[0], a_val, a_inv);
|
|
q[1] = comp_divide8(q[1], a_val, a_inv);
|
|
q[2] = comp_divide8(q[2], a_val, a_inv);
|
|
}
|
|
q += 4;
|
|
}
|
|
}
|
|
|
|
static void gray_to_rgb24(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int x, y_val, c, rnd, shift;
|
|
|
|
if (s->bit_depth == 8 && !s->limited_range) {
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x];
|
|
q[0] = y_val;
|
|
q[1] = y_val;
|
|
q[2] = y_val;
|
|
q += incr;
|
|
}
|
|
} else {
|
|
c = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = clamp8((y_ptr[x] * c + rnd) >> shift);
|
|
q[0] = y_val;
|
|
q[1] = y_val;
|
|
q[2] = y_val;
|
|
q += incr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rgb_to_rgb24(ColorConvertState *s, uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int x, c, rnd, shift;
|
|
|
|
if (s->bit_depth == 8 && !s->limited_range) {
|
|
for(x = 0; x < n; x++) {
|
|
q[0] = cr_ptr[x];
|
|
q[1] = y_ptr[x];
|
|
q[2] = cb_ptr[x];
|
|
q += incr;
|
|
}
|
|
} else {
|
|
c = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
q[0] = clamp8((cr_ptr[x] * c + rnd) >> shift);
|
|
q[1] = clamp8((y_ptr[x] * c + rnd) >> shift);
|
|
q[2] = clamp8((cb_ptr[x] * c + rnd) >> shift);
|
|
q += incr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void put_dummy_gray8(uint8_t *dst, int n, int incr)
|
|
{
|
|
int x;
|
|
for(x = 0; x < n; x++) {
|
|
dst[0] = 0xff;
|
|
dst += incr;
|
|
}
|
|
}
|
|
|
|
static void gray_to_gray8(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint8_t *q = dst;
|
|
int x, y_val, c, rnd, shift;
|
|
|
|
if (s->bit_depth == 8) {
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x];
|
|
q[0] = y_val;
|
|
q += incr;
|
|
}
|
|
} else {
|
|
c = s->c_one;
|
|
rnd = s->c_rnd;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = (y_ptr[x] * c + rnd) >> shift;
|
|
q[0] = y_val;
|
|
q += incr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ColorConvertFunc *cs_to_rgb24[BPG_CS_COUNT] = {
|
|
ycc_to_rgb24,
|
|
rgb_to_rgb24,
|
|
ycgco_to_rgb24,
|
|
ycc_to_rgb24,
|
|
ycc_to_rgb24,
|
|
};
|
|
|
|
#ifdef USE_RGB48
|
|
|
|
/* 16 bit output */
|
|
|
|
static inline int clamp16(int a)
|
|
{
|
|
if (a < 0)
|
|
return 0;
|
|
else if (a > 65535)
|
|
return 65535;
|
|
else
|
|
return a;
|
|
}
|
|
|
|
static void ycc_to_rgb48(ColorConvertState *s, uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint16_t *q = (uint16_t *)dst;
|
|
int y_val, cb_val, cr_val, x;
|
|
int c_r_cr, c_g_cb, c_g_cr, c_b_cb, rnd, shift, center, c_one;
|
|
|
|
c_r_cr = s->c_r_cr;
|
|
c_g_cb = s->c_g_cb;
|
|
c_g_cr = s->c_g_cr;
|
|
c_b_cb = s->c_b_cb;
|
|
c_one = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
center = s->c_center;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x] * c_one;
|
|
cb_val = cb_ptr[x] - center;
|
|
cr_val = cr_ptr[x] - center;
|
|
q[0] = clamp16((y_val + c_r_cr * cr_val + rnd) >> shift);
|
|
q[1] = clamp16((y_val - c_g_cb * cb_val - c_g_cr * cr_val + rnd) >> shift);
|
|
q[2] = clamp16((y_val + c_b_cb * cb_val + rnd) >> shift);
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void ycgco_to_rgb48(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint16_t *q = (uint16_t *)dst;
|
|
int y_val, cb_val, cr_val, x;
|
|
int rnd, shift, center, c_one;
|
|
|
|
c_one = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
center = s->c_center;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = y_ptr[x];
|
|
cb_val = cb_ptr[x] - center;
|
|
cr_val = cr_ptr[x] - center;
|
|
q[0] = clamp16(((y_val - cb_val + cr_val) * c_one + rnd) >> shift);
|
|
q[1] = clamp16(((y_val + cb_val) * c_one + rnd) >> shift);
|
|
q[2] = clamp16(((y_val - cb_val - cr_val) * c_one + rnd) >> shift);
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void gray_to_rgb48(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint16_t *q = (uint16_t *)dst;
|
|
int x, y_val, c, rnd, shift;
|
|
|
|
c = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = clamp16((y_ptr[x] * c + rnd) >> shift);
|
|
q[0] = y_val;
|
|
q[1] = y_val;
|
|
q[2] = y_val;
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void gray_to_gray16(ColorConvertState *s,
|
|
uint16_t *dst, const PIXEL *y_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint16_t *q = dst;
|
|
int x, y_val, c, rnd, shift;
|
|
|
|
c = s->c_one;
|
|
rnd = s->c_rnd;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = (y_ptr[x] * c + rnd) >> shift;
|
|
q[0] = y_val;
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void luma_to_gray16(ColorConvertState *s,
|
|
uint16_t *dst, const PIXEL *y_ptr,
|
|
int n, int incr)
|
|
{
|
|
uint16_t *q = dst;
|
|
int x, y_val, c, rnd, shift;
|
|
|
|
c = s->y_one;
|
|
rnd = s->y_offset;
|
|
shift = s->c_shift;
|
|
for(x = 0; x < n; x++) {
|
|
y_val = clamp16((y_ptr[x] * c + rnd) >> shift);
|
|
q[0] = y_val;
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
static void rgb_to_rgb48(ColorConvertState *s,
|
|
uint8_t *dst, const PIXEL *y_ptr,
|
|
const PIXEL *cb_ptr, const PIXEL *cr_ptr,
|
|
int n, int incr)
|
|
{
|
|
luma_to_gray16(s, (uint16_t *)dst + 1, y_ptr, n, incr);
|
|
luma_to_gray16(s, (uint16_t *)dst + 2, cb_ptr, n, incr);
|
|
luma_to_gray16(s, (uint16_t *)dst + 0, cr_ptr, n, incr);
|
|
}
|
|
|
|
static void put_dummy_gray16(uint16_t *dst, int n, int incr)
|
|
{
|
|
int x;
|
|
for(x = 0; x < n; x++) {
|
|
dst[0] = 0xffff;
|
|
dst += incr;
|
|
}
|
|
}
|
|
|
|
/* c = c * alpha */
|
|
static void alpha_combine16(ColorConvertState *s,
|
|
uint16_t *dst, const PIXEL *a_ptr, int n, int incr)
|
|
{
|
|
uint16_t *q = dst;
|
|
int x, a_val, shift, rnd;
|
|
|
|
shift = s->bit_depth;
|
|
rnd = 1 << (shift - 1);
|
|
for(x = 0; x < n; x++) {
|
|
a_val = a_ptr[x];
|
|
/* XXX: not accurate enough */
|
|
q[0] = (q[0] * a_val + rnd) >> shift;
|
|
q[1] = (q[1] * a_val + rnd) >> shift;
|
|
q[2] = (q[2] * a_val + rnd) >> shift;
|
|
q += incr;
|
|
}
|
|
}
|
|
|
|
#define DIV16_BITS 15
|
|
|
|
static unsigned int comp_divide16(unsigned int val, unsigned int alpha,
|
|
unsigned int alpha_inv)
|
|
{
|
|
if (val >= alpha)
|
|
return 65535;
|
|
return (val * alpha_inv + (1 << (DIV16_BITS - 1))) >> DIV16_BITS;
|
|
}
|
|
|
|
/* c = c / alpha */
|
|
static void alpha_divide16(uint16_t *dst, int n)
|
|
{
|
|
uint16_t *q = dst;
|
|
int x;
|
|
unsigned int a_val, a_inv;
|
|
|
|
for(x = 0; x < n; x++) {
|
|
a_val = q[3];
|
|
if (a_val == 0) {
|
|
q[0] = 65535;
|
|
q[1] = 65535;
|
|
q[2] = 65535;
|
|
} else {
|
|
a_inv = ((65535 << DIV16_BITS) + (a_val / 2)) / a_val;
|
|
q[0] = comp_divide16(q[0], a_val, a_inv);
|
|
q[1] = comp_divide16(q[1], a_val, a_inv);
|
|
q[2] = comp_divide16(q[2], a_val, a_inv);
|
|
}
|
|
q += 4;
|
|
}
|
|
}
|
|
|
|
static ColorConvertFunc *cs_to_rgb48[BPG_CS_COUNT] = {
|
|
ycc_to_rgb48,
|
|
rgb_to_rgb48,
|
|
ycgco_to_rgb48,
|
|
ycc_to_rgb48,
|
|
ycc_to_rgb48,
|
|
};
|
|
#endif
|
|
|
|
static void convert_init(ColorConvertState *s,
|
|
int in_bit_depth, int out_bit_depth,
|
|
BPGColorSpaceEnum color_space,
|
|
int limited_range)
|
|
{
|
|
int c_shift, in_pixel_max, out_pixel_max;
|
|
double mult, k_r, k_b, mult_y, mult_c;
|
|
|
|
c_shift = 30 - out_bit_depth;
|
|
in_pixel_max = (1 << in_bit_depth) - 1;
|
|
out_pixel_max = (1 << out_bit_depth) - 1;
|
|
mult = (double)out_pixel_max * (1 << c_shift) / (double)in_pixel_max;
|
|
if (limited_range) {
|
|
mult_y = (double)out_pixel_max * (1 << c_shift) /
|
|
(double)(219 << (in_bit_depth - 8));
|
|
mult_c = (double)out_pixel_max * (1 << c_shift) /
|
|
(double)(224 << (in_bit_depth - 8));
|
|
} else {
|
|
mult_y = mult;
|
|
mult_c = mult;
|
|
}
|
|
switch(color_space) {
|
|
case BPG_CS_YCbCr:
|
|
k_r = 0.299;
|
|
k_b = 0.114;
|
|
goto convert_ycc;
|
|
case BPG_CS_YCbCr_BT709:
|
|
k_r = 0.2126;
|
|
k_b = 0.0722;
|
|
goto convert_ycc;
|
|
case BPG_CS_YCbCr_BT2020:
|
|
k_r = 0.2627;
|
|
k_b = 0.0593;
|
|
convert_ycc:
|
|
s->c_r_cr = lrint(2*(1-k_r) * mult_c);
|
|
s->c_g_cb = lrint(2*k_b*(1-k_b)/(1-k_b-k_r) * mult_c);
|
|
s->c_g_cr = lrint(2*k_r*(1-k_r)/(1-k_b-k_r) * mult_c);
|
|
s->c_b_cb = lrint(2*(1-k_b) * mult_c);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
s->c_one = lrint(mult);
|
|
s->c_shift = c_shift;
|
|
s->c_rnd = (1 << (c_shift - 1));
|
|
s->c_center = 1 << (in_bit_depth - 1);
|
|
if (limited_range) {
|
|
s->y_one = lrint(mult_y);
|
|
s->y_offset = -(16 << (in_bit_depth - 8)) * s->y_one + s->c_rnd;
|
|
} else {
|
|
s->y_one = s->c_one;
|
|
s->y_offset = s->c_rnd;
|
|
}
|
|
s->bit_depth = in_bit_depth;
|
|
s->limited_range = limited_range;
|
|
}
|
|
|
|
static int bpg_decoder_output_init(BPGDecoderContext *s,
|
|
BPGDecoderOutputFormat out_fmt)
|
|
{
|
|
int i, y1, c_idx;
|
|
PIXEL *cb_ptr, *cr_ptr;
|
|
|
|
#ifdef USE_RGB48
|
|
if ((unsigned)out_fmt > BPG_OUTPUT_FORMAT_RGBA64)
|
|
return -1;
|
|
#else
|
|
if ((unsigned)out_fmt > BPG_OUTPUT_FORMAT_RGBA32)
|
|
return -1;
|
|
#endif
|
|
s->is_rgba = (out_fmt == BPG_OUTPUT_FORMAT_RGBA32 ||
|
|
out_fmt == BPG_OUTPUT_FORMAT_RGBA64);
|
|
s->is_rgb48 = (out_fmt == BPG_OUTPUT_FORMAT_RGB48 ||
|
|
out_fmt == BPG_OUTPUT_FORMAT_RGBA64);
|
|
|
|
s->y_buf = bpg_decoder_get_data(s, &s->y_linesize, 0);
|
|
if (s->format != BPG_FORMAT_GRAY) {
|
|
s->cb_buf = bpg_decoder_get_data(s, &s->cb_linesize, 1);
|
|
s->cr_buf = bpg_decoder_get_data(s, &s->cr_linesize, 2);
|
|
c_idx = 3;
|
|
} else {
|
|
c_idx = 1;
|
|
}
|
|
if (s->has_alpha)
|
|
s->a_buf = bpg_decoder_get_data(s, &s->a_linesize, c_idx);
|
|
else
|
|
s->a_buf = NULL;
|
|
|
|
if (s->format == BPG_FORMAT_420 || s->format == BPG_FORMAT_422) {
|
|
s->w2 = (s->w + 1) / 2;
|
|
s->h2 = (s->h + 1) / 2;
|
|
s->cb_buf2 = av_malloc(s->w * sizeof(PIXEL));
|
|
s->cr_buf2 = av_malloc(s->w * sizeof(PIXEL));
|
|
|
|
if (s->format == BPG_FORMAT_420) {
|
|
for(i = 0; i < ITAPS; i++) {
|
|
s->cb_buf3[i] = av_malloc(s->w2 * sizeof(PIXEL));
|
|
s->cr_buf3[i] = av_malloc(s->w2 * sizeof(PIXEL));
|
|
}
|
|
s->c_buf4 = av_malloc((s->w2 + 2 * ITAPS2 - 1) * sizeof(int16_t));
|
|
|
|
/* init the vertical interpolation buffer */
|
|
for(i = 0; i < ITAPS; i++) {
|
|
y1 = i;
|
|
if (y1 > ITAPS2)
|
|
y1 -= ITAPS;
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
else if (y1 >= s->h2)
|
|
y1 = s->h2 - 1;
|
|
cb_ptr = (PIXEL *)(s->cb_buf + y1 * s->cb_linesize);
|
|
cr_ptr = (PIXEL *)(s->cr_buf + y1 * s->cr_linesize);
|
|
memcpy(s->cb_buf3[i], cb_ptr, s->w2 * sizeof(PIXEL));
|
|
memcpy(s->cr_buf3[i], cr_ptr, s->w2 * sizeof(PIXEL));
|
|
}
|
|
}
|
|
}
|
|
convert_init(&s->cvt, s->bit_depth, s->is_rgb48 ? 16 : 8,
|
|
s->color_space, s->limited_range);
|
|
|
|
if (s->format == BPG_FORMAT_GRAY) {
|
|
#ifdef USE_RGB48
|
|
if (s->is_rgb48) {
|
|
s->cvt_func = gray_to_rgb48;
|
|
} else
|
|
#endif
|
|
{
|
|
s->cvt_func = gray_to_rgb24;
|
|
}
|
|
} else {
|
|
#ifdef USE_RGB48
|
|
if (s->is_rgb48) {
|
|
s->cvt_func = cs_to_rgb48[s->color_space];
|
|
} else
|
|
#endif
|
|
{
|
|
s->cvt_func = cs_to_rgb24[s->color_space];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void bpg_decoder_output_end(BPGDecoderContext *s)
|
|
{
|
|
int i;
|
|
|
|
av_free(s->cb_buf2);
|
|
av_free(s->cr_buf2);
|
|
for(i = 0; i < ITAPS; i++) {
|
|
av_free(s->cb_buf3[i]);
|
|
av_free(s->cr_buf3[i]);
|
|
}
|
|
av_free(s->c_buf4);
|
|
}
|
|
|
|
int bpg_decoder_get_line(BPGDecoderContext *s, void *rgb_line1)
|
|
{
|
|
uint8_t *rgb_line = rgb_line1;
|
|
int w, h, y, pos, y2, y1, incr;
|
|
PIXEL *y_ptr, *cb_ptr, *cr_ptr, *a_ptr;
|
|
|
|
w = s->w;
|
|
h = s->h;
|
|
y = s->y;
|
|
|
|
if ((unsigned)y >= h)
|
|
return -1;
|
|
|
|
y_ptr = (PIXEL *)(s->y_buf + y * s->y_linesize);
|
|
incr = 3 + s->is_rgba;
|
|
switch(s->format) {
|
|
case BPG_FORMAT_GRAY:
|
|
s->cvt_func(&s->cvt, rgb_line, y_ptr, NULL, NULL, w, incr);
|
|
break;
|
|
case BPG_FORMAT_420:
|
|
y2 = y >> 1;
|
|
pos = y2 % ITAPS;
|
|
if ((y & 1) == 0) {
|
|
interp2_vh(s->cb_buf2, s->cb_buf3, w, pos, s->c_buf4,
|
|
s->bit_depth, 0);
|
|
interp2_vh(s->cr_buf2, s->cr_buf3, w, pos, s->c_buf4,
|
|
s->bit_depth, 0);
|
|
} else {
|
|
interp2_vh(s->cb_buf2, s->cb_buf3, w, pos, s->c_buf4,
|
|
s->bit_depth, 1);
|
|
interp2_vh(s->cr_buf2, s->cr_buf3, w, pos, s->c_buf4,
|
|
s->bit_depth, 1);
|
|
|
|
/* add a new line in the circular buffer */
|
|
pos = (pos + ITAPS2 + 1) % ITAPS;
|
|
y1 = y2 + ITAPS2 + 1;
|
|
if (y1 >= s->h2)
|
|
y1 = s->h2 - 1;
|
|
cb_ptr = (PIXEL *)(s->cb_buf + y1 * s->cb_linesize);
|
|
cr_ptr = (PIXEL *)(s->cr_buf + y1 * s->cr_linesize);
|
|
memcpy(s->cb_buf3[pos], cb_ptr, s->w2 * sizeof(PIXEL));
|
|
memcpy(s->cr_buf3[pos], cr_ptr, s->w2 * sizeof(PIXEL));
|
|
}
|
|
s->cvt_func(&s->cvt, rgb_line, y_ptr, s->cb_buf2, s->cr_buf2, w, incr);
|
|
break;
|
|
case BPG_FORMAT_422:
|
|
cb_ptr = (PIXEL *)(s->cb_buf + y * s->cb_linesize);
|
|
cr_ptr = (PIXEL *)(s->cr_buf + y * s->cr_linesize);
|
|
interp2_h(s->cb_buf2, cb_ptr, w, s->bit_depth);
|
|
interp2_h(s->cr_buf2, cr_ptr, w, s->bit_depth);
|
|
s->cvt_func(&s->cvt, rgb_line, y_ptr, s->cb_buf2, s->cr_buf2, w, incr);
|
|
break;
|
|
case BPG_FORMAT_444:
|
|
cb_ptr = (PIXEL *)(s->cb_buf + y * s->cb_linesize);
|
|
cr_ptr = (PIXEL *)(s->cr_buf + y * s->cr_linesize);
|
|
s->cvt_func(&s->cvt, rgb_line, y_ptr, cb_ptr, cr_ptr, w, incr);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
/* alpha output or CMYK handling */
|
|
if (s->has_w_plane) {
|
|
a_ptr = (PIXEL *)(s->a_buf + y * s->a_linesize);
|
|
#ifdef USE_RGB48
|
|
if (s->is_rgb48) {
|
|
alpha_combine16(&s->cvt, (uint16_t *)rgb_line, a_ptr, w, incr);
|
|
if (s->is_rgba)
|
|
put_dummy_gray16((uint16_t *)rgb_line + 3, w, 4);
|
|
} else
|
|
#endif
|
|
{
|
|
alpha_combine8(&s->cvt, rgb_line, a_ptr, w, incr);
|
|
if (s->is_rgba)
|
|
put_dummy_gray8(rgb_line + 3, w, 4);
|
|
}
|
|
} else {
|
|
if (s->is_rgba) {
|
|
#ifdef USE_RGB48
|
|
if (s->is_rgb48) {
|
|
if (s->has_alpha) {
|
|
a_ptr = (PIXEL *)(s->a_buf + y * s->a_linesize);
|
|
gray_to_gray16(&s->cvt,
|
|
(uint16_t *)rgb_line + 3, a_ptr, w, 4);
|
|
if (s->premultiplied_alpha)
|
|
alpha_divide16((uint16_t *)rgb_line, w);
|
|
} else {
|
|
put_dummy_gray16((uint16_t *)rgb_line + 3, w, 4);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (s->has_alpha) {
|
|
a_ptr = (PIXEL *)(s->a_buf + y * s->a_linesize);
|
|
gray_to_gray8(&s->cvt, rgb_line + 3, a_ptr, w, 4);
|
|
if (s->premultiplied_alpha)
|
|
alpha_divide8((uint8_t *)rgb_line, w);
|
|
} else {
|
|
put_dummy_gray8(rgb_line + 3, w, 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* go to next line */
|
|
s->y++;
|
|
return 0;
|
|
}
|
|
|
|
int bpg_decoder_start(BPGDecoderContext *s, BPGDecoderOutputFormat out_fmt)
|
|
{
|
|
int ret;
|
|
|
|
if (!s->frame || s->y >= 0)
|
|
return -1;
|
|
ret = bpg_decoder_output_init(s, out_fmt);
|
|
if (ret)
|
|
return ret;
|
|
s->y = 0;
|
|
return 0;
|
|
}
|
|
|
|
BPGDecoderContext *bpg_decoder_open(void)
|
|
{
|
|
BPGDecoderContext *s;
|
|
|
|
s = av_mallocz(sizeof(BPGDecoderContext));
|
|
if (!s)
|
|
return NULL;
|
|
return s;
|
|
}
|
|
|
|
typedef struct {
|
|
uint32_t width, height;
|
|
BPGImageFormatEnum format;
|
|
uint8_t has_alpha;
|
|
uint8_t bit_depth;
|
|
uint8_t has_w_plane;
|
|
uint8_t premultiplied_alpha;
|
|
uint8_t limited_range;
|
|
BPGColorSpaceEnum color_space;
|
|
uint32_t ycc_data_len;
|
|
uint32_t alpha_data_len;
|
|
BPGExtensionData *first_md;
|
|
} BPGHeaderData;
|
|
|
|
static int bpg_decode_header(BPGHeaderData *h,
|
|
const uint8_t *buf, int buf_len,
|
|
int header_only, int load_extensions)
|
|
{
|
|
int idx, flags1, flags2, has_extension, ret, alpha1_flag, alpha2_flag;
|
|
uint32_t extension_data_len;
|
|
|
|
if (buf_len < 6)
|
|
return -1;
|
|
/* check magic */
|
|
if (buf[0] != ((BPG_HEADER_MAGIC >> 24) & 0xff) ||
|
|
buf[1] != ((BPG_HEADER_MAGIC >> 16) & 0xff) ||
|
|
buf[2] != ((BPG_HEADER_MAGIC >> 8) & 0xff) ||
|
|
buf[3] != ((BPG_HEADER_MAGIC >> 0) & 0xff))
|
|
return -1;
|
|
idx = 4;
|
|
flags1 = buf[idx++];
|
|
h->format = flags1 >> 5;
|
|
if (h->format > 3)
|
|
return -1;
|
|
alpha1_flag = (flags1 >> 4) & 1;
|
|
h->bit_depth = (flags1 & 0xf) + 8;
|
|
if (h->bit_depth > 14)
|
|
return -1;
|
|
flags2 = buf[idx++];
|
|
h->color_space = (flags2 >> 4) & 0xf;
|
|
has_extension = (flags2 >> 3) & 1;
|
|
alpha2_flag = (flags2 >> 2) & 1;
|
|
h->limited_range = (flags2 >> 1) & 1;
|
|
|
|
h->has_alpha = 0;
|
|
h->has_w_plane = 0;
|
|
h->premultiplied_alpha = 0;
|
|
|
|
if (alpha1_flag) {
|
|
h->has_alpha = 1;
|
|
h->premultiplied_alpha = alpha2_flag;
|
|
} else if (alpha2_flag) {
|
|
h->has_alpha = 1;
|
|
h->has_w_plane = 1;
|
|
}
|
|
|
|
if (h->color_space >= BPG_CS_COUNT ||
|
|
(h->format == BPG_FORMAT_GRAY && h->color_space != 0) ||
|
|
(h->has_w_plane && h->format == BPG_FORMAT_GRAY))
|
|
return -1;
|
|
ret = get_ue(&h->width, buf + idx, buf_len - idx);
|
|
if (ret < 0)
|
|
return -1;
|
|
idx += ret;
|
|
ret = get_ue(&h->height, buf + idx, buf_len - idx);
|
|
if (ret < 0)
|
|
return -1;
|
|
idx += ret;
|
|
if (h->width == 0 || h->height == 0)
|
|
return -1;
|
|
if (header_only)
|
|
return idx;
|
|
|
|
ret = get_ue(&h->ycc_data_len, buf + idx, buf_len - idx);
|
|
if (ret < 0)
|
|
return -1;
|
|
idx += ret;
|
|
|
|
extension_data_len = 0;
|
|
if (has_extension) {
|
|
ret = get_ue(&extension_data_len, buf + idx, buf_len - idx);
|
|
if (ret < 0)
|
|
return -1;
|
|
idx += ret;
|
|
}
|
|
|
|
h->alpha_data_len = 0;
|
|
if (h->has_alpha) {
|
|
ret = get_ue(&h->alpha_data_len, buf + idx, buf_len - idx);
|
|
if (ret < 0)
|
|
return -1;
|
|
idx += ret;
|
|
}
|
|
|
|
h->first_md = NULL;
|
|
if (has_extension) {
|
|
int ext_end;
|
|
|
|
ext_end = idx + extension_data_len;
|
|
if (ext_end > buf_len)
|
|
return -1;
|
|
#ifndef EMSCRIPTEN
|
|
if (load_extensions) {
|
|
BPGExtensionData *md, **plast_md;
|
|
|
|
plast_md = &h->first_md;
|
|
while (idx < ext_end) {
|
|
md = av_malloc(sizeof(BPGExtensionData));
|
|
*plast_md = md;
|
|
plast_md = &md->next;
|
|
|
|
ret = get_ue32(&md->tag, buf + idx, ext_end - idx);
|
|
if (ret < 0)
|
|
goto fail;
|
|
idx += ret;
|
|
|
|
ret = get_ue(&md->buf_len, buf + idx, ext_end - idx);
|
|
if (ret < 0)
|
|
goto fail;
|
|
idx += ret;
|
|
|
|
if (idx + md->buf_len > ext_end) {
|
|
fail:
|
|
bpg_decoder_free_extension_data(h->first_md);
|
|
return -1;
|
|
}
|
|
md->buf = av_malloc(md->buf_len);
|
|
memcpy(md->buf, buf + idx, md->buf_len);
|
|
idx += md->buf_len;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
/* skip extension data */
|
|
idx += extension_data_len;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
int bpg_decoder_decode(BPGDecoderContext *img, const uint8_t *buf, int buf_len)
|
|
{
|
|
int idx, has_alpha, format, bit_depth, chroma_format_idc, color_space;
|
|
uint32_t width, height;
|
|
BPGHeaderData h_s, *h = &h_s;
|
|
|
|
idx = bpg_decode_header(h, buf, buf_len, 0, img->keep_extension_data);
|
|
if (idx < 0)
|
|
return idx;
|
|
width = h->width;
|
|
height = h->height;
|
|
format = h->format;
|
|
has_alpha = h->has_alpha;
|
|
color_space = h->color_space;
|
|
bit_depth = h->bit_depth;
|
|
|
|
img->w = width;
|
|
img->h = height;
|
|
img->format = format;
|
|
img->has_alpha = has_alpha;
|
|
img->premultiplied_alpha = h->premultiplied_alpha;
|
|
img->has_w_plane = h->has_w_plane;
|
|
img->limited_range = h->limited_range;
|
|
img->color_space = color_space;
|
|
img->bit_depth = bit_depth;
|
|
img->first_md = h->first_md;
|
|
|
|
if (idx + h->ycc_data_len > buf_len)
|
|
goto fail;
|
|
chroma_format_idc = format;
|
|
img->frame = hevc_decode(buf + idx, h->ycc_data_len,
|
|
width, height, chroma_format_idc, bit_depth);
|
|
if (!img->frame)
|
|
goto fail;
|
|
idx += h->ycc_data_len;
|
|
|
|
if (img->frame->width < img->w || img->frame->height < img->h)
|
|
goto fail;
|
|
|
|
switch(img->frame->format) {
|
|
case AV_PIX_FMT_YUV420P16:
|
|
case AV_PIX_FMT_YUV420P:
|
|
if (format != BPG_FORMAT_420)
|
|
goto fail;
|
|
break;
|
|
case AV_PIX_FMT_YUV422P16:
|
|
case AV_PIX_FMT_YUV422P:
|
|
if (format != BPG_FORMAT_422)
|
|
goto fail;
|
|
break;
|
|
case AV_PIX_FMT_YUV444P16:
|
|
case AV_PIX_FMT_YUV444P:
|
|
if (format != BPG_FORMAT_444)
|
|
goto fail;
|
|
break;
|
|
case AV_PIX_FMT_GRAY16:
|
|
case AV_PIX_FMT_GRAY8:
|
|
if (format != BPG_FORMAT_GRAY)
|
|
goto fail;
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
|
|
if (has_alpha) {
|
|
if (idx + h->alpha_data_len > buf_len)
|
|
goto fail;
|
|
img->alpha_frame = hevc_decode(buf + idx, h->alpha_data_len,
|
|
width, height, 0, bit_depth);
|
|
if (!img->alpha_frame)
|
|
goto fail;
|
|
idx += h->alpha_data_len;
|
|
}
|
|
|
|
img->y = -1;
|
|
return 0;
|
|
|
|
fail:
|
|
if (img->frame)
|
|
av_frame_free(&img->frame);
|
|
if (img->alpha_frame)
|
|
av_frame_free(&img->alpha_frame);
|
|
bpg_decoder_free_extension_data(img->first_md);
|
|
img->first_md = NULL;
|
|
return -1;
|
|
}
|
|
|
|
void bpg_decoder_close(BPGDecoderContext *s)
|
|
{
|
|
bpg_decoder_output_end(s);
|
|
if (s->frame)
|
|
av_frame_free(&s->frame);
|
|
if (s->alpha_frame)
|
|
av_frame_free(&s->alpha_frame);
|
|
bpg_decoder_free_extension_data(s->first_md);
|
|
av_free(s);
|
|
}
|
|
|
|
void bpg_decoder_free_extension_data(BPGExtensionData *first_md)
|
|
{
|
|
#ifndef EMSCRIPTEN
|
|
BPGExtensionData *md, *md_next;
|
|
|
|
for(md = first_md; md != NULL; md = md_next) {
|
|
md_next = md->next;
|
|
av_free(md->buf);
|
|
av_free(md);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifndef EMSCRIPTEN
|
|
void bpg_decoder_keep_extension_data(BPGDecoderContext *s, int enable)
|
|
{
|
|
s->keep_extension_data = enable;
|
|
}
|
|
|
|
BPGExtensionData *bpg_decoder_get_extension_data(BPGDecoderContext *s)
|
|
{
|
|
return s->first_md;
|
|
}
|
|
|
|
int bpg_decoder_get_info_from_buf(BPGImageInfo *p,
|
|
BPGExtensionData **pfirst_md,
|
|
const uint8_t *buf, int buf_len)
|
|
{
|
|
BPGHeaderData h_s, *h = &h_s;
|
|
int parse_extension;
|
|
|
|
parse_extension = (pfirst_md != NULL);
|
|
if (bpg_decode_header(h, buf, buf_len,
|
|
!parse_extension, parse_extension) < 0)
|
|
return -1;
|
|
p->width = h->width;
|
|
p->height = h->height;
|
|
p->format = h->format;
|
|
p->has_alpha = h->has_alpha && !h->has_w_plane;
|
|
p->premultiplied_alpha = h->premultiplied_alpha;
|
|
p->has_w_plane = h->has_w_plane;
|
|
p->limited_range = h->limited_range;
|
|
p->color_space = h->color_space;
|
|
p->bit_depth = h->bit_depth;
|
|
if (pfirst_md)
|
|
*pfirst_md = h->first_md;
|
|
return 0;
|
|
}
|
|
#endif
|