lavc: add xface image decoder and encoder

Based on libcompface code by James Ashton <James.Ashton@anu.edu.au>, and
relicensed to LGPL with the author's consent.
pull/6/head
Stefano Sabatini 13 years ago
parent 35782bfbca
commit cd559bb49a
  1. 1
      Changelog
  2. 2
      doc/general.texi
  3. 2
      libavcodec/Makefile
  4. 1
      libavcodec/allcodecs.c
  5. 1
      libavcodec/avcodec.h
  6. 6
      libavcodec/codec_desc.c
  7. 2
      libavcodec/version.h
  8. 381
      libavcodec/xface.c
  9. 95
      libavcodec/xface.h
  10. 207
      libavcodec/xfacedec.c
  11. 237
      libavcodec/xfaceenc.c
  12. 1
      libavformat/img2.c
  13. 2
      libavformat/img2enc.c
  14. 2
      libavformat/version.h

@ -11,6 +11,7 @@ version next:
- TAK demuxer, decoder and parser
- DTS-HD demuxer
- remove -same_quant, it hasn't worked for years
- X-Face image encoder and decoder
version 1.0:

@ -441,6 +441,8 @@ following image formats are supported:
@tab Targa (.TGA) image format
@item XBM @tab X @tab X
@tab X BitMap image format
@item XFace @tab X @tab X
@tab X-Face image format
@item XWD @tab X @tab X
@tab X Window Dump image format
@end multitable

@ -482,6 +482,8 @@ OBJS-$(CONFIG_XAN_WC4_DECODER) += xxan.o
OBJS-$(CONFIG_XBIN_DECODER) += bintext.o cga_data.o
OBJS-$(CONFIG_XBM_DECODER) += xbmdec.o
OBJS-$(CONFIG_XBM_ENCODER) += xbmenc.o
OBJS-$(CONFIG_XFACE_DECODER) += xfacedec.o xface.o
OBJS-$(CONFIG_XFACE_ENCODER) += xfaceenc.o xface.o
OBJS-$(CONFIG_XL_DECODER) += xl.o
OBJS-$(CONFIG_XSUB_DECODER) += xsubdec.o
OBJS-$(CONFIG_XSUB_ENCODER) += xsubenc.o

@ -265,6 +265,7 @@ void avcodec_register_all(void)
REGISTER_DECODER (XAN_WC3, xan_wc3);
REGISTER_DECODER (XAN_WC4, xan_wc4);
REGISTER_ENCDEC (XBM, xbm);
REGISTER_ENCDEC (XFACE, xface);
REGISTER_DECODER (XL, xl);
REGISTER_ENCDEC (XWD, xwd);
REGISTER_ENCDEC (Y41P, y41p);

@ -283,6 +283,7 @@ enum AVCodecID {
AV_CODEC_ID_PAF_VIDEO = MKBETAG('P','A','F','V'),
AV_CODEC_ID_AVRN = MKBETAG('A','V','R','n'),
AV_CODEC_ID_CPIA = MKBETAG('C','P','I','A'),
AV_CODEC_ID_XFACE = MKBETAG('X','F','A','C'),
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs

@ -1315,6 +1315,12 @@ static const AVCodecDescriptor codec_descriptors[] = {
.name = "cpia",
.long_name = NULL_IF_CONFIG_SMALL("CPiA video format"),
},
{
.id = AV_CODEC_ID_XFACE,
.type = AVMEDIA_TYPE_VIDEO,
.name = "xface",
.long_name = NULL_IF_CONFIG_SMALL("X-face image"),
},
/* various PCM "codecs" */
{

@ -29,7 +29,7 @@
#include "libavutil/avutil.h"
#define LIBAVCODEC_VERSION_MAJOR 54
#define LIBAVCODEC_VERSION_MINOR 65
#define LIBAVCODEC_VERSION_MINOR 66
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \

@ -0,0 +1,381 @@
/*
* Copyright (c) 1990 James Ashton - Sydney University
* Copyright (c) 2012 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* X-Face common data and utilities definition.
*/
#include "xface.h"
void ff_big_add(BigInt *b, uint8_t a)
{
int i;
uint8_t *w;
uint16_t c;
a &= XFACE_WORDMASK;
if (a == 0)
return;
w = b->words;
c = a;
for (i = 0; i < b->nb_words && c; i++) {
c += *w;
*w++ = c & XFACE_WORDMASK;
c >>= XFACE_BITSPERWORD;
}
if (i == b->nb_words && c) {
b->nb_words++;
*w = c & XFACE_WORDMASK;
}
}
void ff_big_div(BigInt *b, uint8_t a, uint8_t *r)
{
int i;
uint8_t *w;
uint16_t c, d;
a &= XFACE_WORDMASK;
if (a == 1 || b->nb_words == 0) {
*r = 0;
return;
}
/* treat this as a == WORDCARRY and just shift everything right a WORD */
if (a == 0) {
i = --b->nb_words;
w = b->words;
*r = *w;
while (i--) {
*w = *(w + 1);
w++;
}
*w = 0;
return;
}
i = b->nb_words;
w = b->words + i;
c = 0;
while (i--) {
c <<= XFACE_BITSPERWORD;
c += *--w;
d = c / (uint16_t)a;
c = c % (uint16_t)a;
*w = d & XFACE_WORDMASK;
}
*r = c;
if (b->words[b->nb_words - 1] == 0)
b->nb_words--;
}
void ff_big_mul(BigInt *b, uint8_t a)
{
int i;
uint8_t *w;
uint16_t c;
a &= XFACE_WORDMASK;
if (a == 1 || b->nb_words == 0)
return;
if (a == 0) {
/* treat this as a == WORDCARRY and just shift everything left a WORD */
i = b->nb_words++;
w = b->words + i;
while (i--) {
*w = *(w - 1);
w--;
}
*w = 0;
return;
}
i = b->nb_words;
w = b->words;
c = 0;
while (i--) {
c += (uint16_t)*w * (uint16_t)a;
*(w++) = c & XFACE_WORDMASK;
c >>= XFACE_BITSPERWORD;
}
if (c) {
b->nb_words++;
*w = c & XFACE_WORDMASK;
}
}
const ProbRange ff_xface_probranges_per_level[4][3] = {
// black grey white
{ { 1, 255}, {251, 0}, { 4, 251} }, /* Top of tree almost always grey */
{ { 1, 255}, {200, 0}, { 55, 200} },
{ { 33, 223}, {159, 0}, { 64, 159} },
{ {131, 0}, { 0, 0}, {125, 131} }, /* Grey disallowed at bottom */
};
const ProbRange ff_xface_probranges_2x2[16] = {
{ 0, 0}, {38, 0}, {38, 38}, {13, 152},
{38, 76}, {13, 165}, {13, 178}, { 6, 230},
{38, 114}, {13, 191}, {13, 204}, { 6, 236},
{13, 217}, { 6, 242}, { 5, 248}, { 3, 253},
};
/*
* The "guess the next pixel" tables follow. Normally there are 12
* neighbour pixels used to give 1<<12 cases as we get closer to the
* upper left corner lesser numbers of neighbours are available.
*
* Each byte in the tables represents 8 boolean values starting from
* the most significant bit.
*/
static const uint8_t g_00[] = {
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xe3, 0xdf, 0x05, 0x17,
0x05, 0x0f, 0x00, 0x1b, 0x0f, 0xdf, 0x00, 0x04, 0x00, 0x00,
0x0d, 0x0f, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1d,
0x45, 0x2f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x0a, 0xff, 0xff,
0x00, 0x04, 0x00, 0x05, 0x01, 0x3f, 0xcf, 0xff, 0x10, 0x01,
0x80, 0xc9, 0x0f, 0x0f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x1b, 0x1f, 0xff, 0xff, 0x4f, 0x54, 0x07, 0x1f, 0x57, 0x47,
0xd7, 0x3d, 0xff, 0xff, 0x5f, 0x1f, 0x7f, 0xff, 0x7f, 0x7f,
0x05, 0x0f, 0x01, 0x0f, 0x0f, 0x5f, 0x9b, 0xdf, 0x7f, 0xff,
0x5f, 0x1d, 0x5f, 0xff, 0x0f, 0x1f, 0x0f, 0x5f, 0x03, 0x1f,
0x4f, 0x5f, 0xf7, 0x7f, 0x7f, 0xff, 0x0d, 0x0f, 0xfb, 0xff,
0xf7, 0xbf, 0x0f, 0x4f, 0xd7, 0x3f, 0x4f, 0x7f, 0xff, 0xff,
0x67, 0xbf, 0x56, 0x25, 0x1f, 0x7f, 0x9f, 0xff, 0x00, 0x00,
0x00, 0x05, 0x5f, 0x7f, 0x01, 0xdf, 0x14, 0x00, 0x05, 0x0f,
0x07, 0xa2, 0x09, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x5f,
0x18, 0xd7, 0x94, 0x71, 0x00, 0x05, 0x1f, 0xb7, 0x0c, 0x07,
0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x1f, 0x84, 0x8f, 0x05, 0x15,
0x05, 0x0f, 0x4f, 0xff, 0x87, 0xdf, 0x05, 0x01, 0x10, 0x00,
0x0f, 0x0f, 0x00, 0x08, 0x05, 0x04, 0x04, 0x01, 0x4f, 0xff,
0x9f, 0x8f, 0x4a, 0x40, 0x5f, 0x5f, 0xff, 0xfe, 0xdf, 0xff,
0x7f, 0xf7, 0xff, 0x7f, 0xff, 0xff, 0x7b, 0xff, 0x0f, 0xfd,
0xd7, 0x5f, 0x4f, 0x7f, 0x7f, 0xdf, 0xff, 0xff, 0xff, 0xff,
0xff, 0x77, 0xdf, 0x7f, 0x4f, 0xef, 0xff, 0xff, 0x77, 0xff,
0xff, 0xff, 0x6f, 0xff, 0x0f, 0x4f, 0xff, 0xff, 0x9d, 0xff,
0x0f, 0xef, 0xff, 0xdf, 0x6f, 0xff, 0xff, 0xff, 0x4f, 0xff,
0xcd, 0x0f, 0x4f, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x0b,
0x05, 0x02, 0x02, 0x0f, 0x04, 0x00, 0x00, 0x0c, 0x01, 0x06,
0x00, 0x0f, 0x20, 0x03, 0x00, 0x00, 0x05, 0x0f, 0x40, 0x08,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x0c, 0x0f, 0x01, 0x00,
0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x14, 0x01, 0x05,
0x01, 0x15, 0xaf, 0x0f, 0x00, 0x01, 0x10, 0x00, 0x08, 0x00,
0x46, 0x0c, 0x20, 0x00, 0x88, 0x00, 0x0f, 0x15, 0xff, 0xdf,
0x02, 0x00, 0x00, 0x0f, 0x7f, 0x5f, 0xdb, 0xff, 0x4f, 0x3e,
0x05, 0x0f, 0x7f, 0xf7, 0x95, 0x4f, 0x0d, 0x0f, 0x01, 0x0f,
0x4f, 0x5f, 0x9f, 0xdf, 0x25, 0x0e, 0x0d, 0x0d, 0x4f, 0x7f,
0x8f, 0x0f, 0x0f, 0xfa, 0x04, 0x4f, 0x4f, 0xff, 0xf7, 0x77,
0x47, 0xed, 0x05, 0x0f, 0xff, 0xff, 0xdf, 0xff, 0x4f, 0x6f,
0xd8, 0x5f, 0x0f, 0x7f, 0xdf, 0x5f, 0x07, 0x0f, 0x94, 0x0d,
0x1f, 0xff, 0xff, 0xff, 0x00, 0x02, 0x00, 0x03, 0x46, 0x57,
0x01, 0x0d, 0x01, 0x08, 0x01, 0x0f, 0x47, 0x6c, 0x0d, 0x0f,
0x02, 0x00, 0x00, 0x00, 0x0b, 0x4f, 0x00, 0x08, 0x05, 0x00,
0x95, 0x01, 0x0f, 0x7f, 0x0c, 0x0f, 0x01, 0x0e, 0x00, 0x00,
0x0f, 0x41, 0x00, 0x00, 0x04, 0x24, 0x0d, 0x0f, 0x0f, 0x7f,
0xcf, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00,
0x06, 0x26, 0xcf, 0x05, 0xcf, 0x7f, 0xdf, 0xdf, 0x00, 0x00,
0x17, 0x5f, 0xff, 0xfd, 0xff, 0xff, 0x46, 0x09, 0x4f, 0x5f,
0x7f, 0xfd, 0xdf, 0xff, 0x0a, 0x88, 0xa7, 0x7f, 0x7f, 0xff,
0xff, 0xff, 0x0f, 0x04, 0xdf, 0x7f, 0x4f, 0xff, 0x9f, 0xff,
0x0e, 0xe6, 0xdf, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x0f, 0xec,
0x8f, 0x4f, 0x7f, 0xff, 0xdf, 0xff, 0x0f, 0xcf, 0xdf, 0xff,
0x6f, 0x7f, 0xff, 0xff, 0x03, 0x0c, 0x9d, 0x0f, 0x7f, 0xff,
0xff, 0xff,
};
static const uint8_t g_01[] = {
0x37, 0x73, 0x00, 0x19, 0x57, 0x7f, 0xf5, 0xfb, 0x70, 0x33,
0xf0, 0xf9, 0x7f, 0xff, 0xff, 0xff,
};
static const uint8_t g_02[] = {
0x50,
};
static const uint8_t g_10[] = {
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0xf3, 0x5f, 0x84, 0x04,
0x17, 0x9f, 0x04, 0x23, 0x05, 0xff, 0x00, 0x00, 0x00, 0x02,
0x03, 0x03, 0x33, 0xd7, 0x05, 0x03, 0x5f, 0x3f, 0x17, 0x33,
0xff, 0xff, 0x00, 0x80, 0x02, 0x04, 0x12, 0x00, 0x11, 0x57,
0x05, 0x25, 0x05, 0x03, 0x35, 0xbf, 0x9f, 0xff, 0x07, 0x6f,
0x20, 0x40, 0x17, 0x06, 0xfa, 0xe8, 0x01, 0x07, 0x1f, 0x9f,
0x1f, 0xff, 0xff, 0xff,
};
static const uint8_t g_20[] = {
0x04, 0x00, 0x01, 0x01, 0x43, 0x2e, 0xff, 0x3f,
};
static const uint8_t g_30[] = {
0x11, 0x11, 0x11, 0x11, 0x51, 0x11, 0x13, 0x11, 0x11, 0x11,
0x13, 0x11, 0x11, 0x11, 0x33, 0x11, 0x13, 0x11, 0x13, 0x13,
0x13, 0x13, 0x31, 0x31, 0x11, 0x01, 0x11, 0x11, 0x71, 0x11,
0x11, 0x75,
};
static const uint8_t g_40[] = {
0x00, 0x0f, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x0f,
0x00, 0x4e, 0xe4, 0x0d, 0x10, 0x0f, 0x00, 0x0f, 0x44, 0x4f,
0x00, 0x1e, 0x0f, 0x0f, 0xae, 0xaf, 0x45, 0x7f, 0xef, 0xff,
0x0f, 0xff, 0x00, 0x09, 0x01, 0x11, 0x00, 0x01, 0x1c, 0xdd,
0x00, 0x15, 0x00, 0xff, 0x00, 0x10, 0x00, 0xfd, 0x00, 0x0f,
0x4f, 0x5f, 0x3d, 0xff, 0xff, 0xff, 0x4f, 0xff, 0x1c, 0xff,
0xdf, 0xff, 0x8f, 0xff, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x15,
0x01, 0x07, 0x00, 0x01, 0x02, 0x1f, 0x01, 0x11, 0x05, 0x7f,
0x00, 0x1f, 0x41, 0x57, 0x1f, 0xff, 0x05, 0x77, 0x0d, 0x5f,
0x4d, 0xff, 0x4f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x02, 0x05,
0x00, 0x11, 0x05, 0x7d, 0x10, 0x15, 0x2f, 0xff, 0x40, 0x50,
0x0d, 0xfd, 0x04, 0x0f, 0x07, 0x1f, 0x07, 0x7f, 0x0f, 0xbf,
0x0d, 0x7f, 0x0f, 0xff, 0x4d, 0x7d, 0x0f, 0xff,
};
static const uint8_t g_11[] = {
0x01, 0x13, 0x03, 0x7f,
};
static const uint8_t g_21[] = {
0x17,
};
static const uint8_t g_31[] = {
0x55, 0x57, 0x57, 0x7f,
};
static const uint8_t g_41[] = {
0x01, 0x01, 0x01, 0x1f, 0x03, 0x1f, 0x3f, 0xff,
};
static const uint8_t g_12[] = {
0x40,
};
static const uint8_t g_22[] = {
0x00,
};
static const uint8_t g_32[] = {
0x10,
};
static const uint8_t g_42[] = {
0x10,
};
void ff_xface_generate_face(uint8_t *dst, uint8_t * const src)
{
int h, i, j, k, l, m;
for (j = 0; j < XFACE_HEIGHT; j++) {
for (i = 0; i < XFACE_WIDTH; i++) {
h = i + j * XFACE_WIDTH;
k = 0;
/*
Compute k, encoding the bits *before* the current one, contained in the
image buffer. That is, given the grid:
l i
| |
v v
+--+--+--+--+--+
m -> | 1| 2| 3| 4| 5|
+--+--+--+--+--+
| 6| 7| 8| 9|10|
+--+--+--+--+--+
j -> |11|12| *| | |
+--+--+--+--+--+
the value k for the pixel marked as "*" will contain the bit encoding of
the values in the matrix marked from "1" to "12". In case the pixel is
near the border of the grid, the number of values contained within the
grid will be lesser than 12.
*/
for (l = i - 2; l <= i + 2; l++) {
for (m = j - 2; m <= j; m++) {
if (l >= i && m == j)
continue;
if (l > 0 && l <= XFACE_WIDTH && m > 0)
k = 2*k + src[l + m * XFACE_WIDTH];
}
}
/*
Use the guess for the given position and the computed value of k.
The following table shows the number of digits in k, depending on
the position of the pixel, and shows the corresponding guess table
to use:
i=1 i=2 i=3 i=w-1 i=w
+----+----+----+ ... +----+----+
j=1 | 0 | 1 | 2 | | 2 | 2 |
|g22 |g12 |g02 | |g42 |g32 |
+----+----+----+ ... +----+----+
j=2 | 3 | 5 | 7 | | 6 | 5 |
|g21 |g11 |g01 | |g41 |g31 |
+----+----+----+ ... +----+----+
j=3 | 5 | 9 | 12 | | 10 | 8 |
|g20 |g10 |g00 | |g40 |g30 |
+----+----+----+ ... +----+----+
*/
#define GEN(table) dst[h] ^= (table[k>>3]>>(7-(k&7)))&1
switch (i) {
case 1:
switch (j) {
case 1: GEN(g_22); break;
case 2: GEN(g_21); break;
default: GEN(g_20); break;
}
break;
case 2:
switch (j) {
case 1: GEN(g_12); break;
case 2: GEN(g_11); break;
default: GEN(g_10); break;
}
break;
case XFACE_WIDTH - 1:
switch (j) {
case 1: GEN(g_42); break;
case 2: GEN(g_41); break;
default: GEN(g_40); break;
}
break;
case XFACE_WIDTH:
switch (j) {
case 1: GEN(g_32); break;
case 2: GEN(g_31); break;
default: GEN(g_30); break;
}
break;
default:
switch (j) {
case 1: GEN(g_02); break;
case 2: GEN(g_01); break;
default: GEN(g_00); break;
}
break;
}
}
}
}

@ -0,0 +1,95 @@
/*
* Copyright (c) 1990 James Ashton - Sydney University
* Copyright (c) 2012 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* X-Face common definitions.
*/
#include <stdint.h>
/* define the face size - 48x48x1 */
#define XFACE_WIDTH 48
#define XFACE_HEIGHT 48
#define XFACE_PIXELS (XFACE_WIDTH * XFACE_HEIGHT)
/* compressed output uses the full range of printable characters.
* In ASCII these are in a contiguous block so we just need to know
* the first and last. The total number of printables is needed too. */
#define XFACE_FIRST_PRINT '!'
#define XFACE_LAST_PRINT '~'
#define XFACE_PRINTS (XFACE_LAST_PRINT - XFACE_FIRST_PRINT + 1)
/*
* Image is encoded as a big integer, using characters from '~' to
* '!', for a total of 92 symbols. In order to express 48x48=2304
* bits, we need a total of 354 digits, as given by:
* ceil(lg_92(2^2304)) = 354
*/
#define XFACE_MAX_DIGITS 354
#define XFACE_BITSPERWORD 8
#define XFACE_WORDCARRY (1 << XFACE_BITSPERWORD)
#define XFACE_WORDMASK (XFACE_WORDCARRY - 1)
#define XFACE_MAX_WORDS ((XFACE_PIXELS * 2 + XFACE_BITSPERWORD - 1) / XFACE_BITSPERWORD)
/* Portable, very large unsigned integer arithmetic is needed.
* Implementation uses arrays of WORDs. */
typedef struct {
int nb_words;
uint8_t words[XFACE_MAX_WORDS];
} BigInt;
/**
* Add a to b storing the result in b.
*/
void ff_big_add(BigInt *b, uint8_t a);
/**
* Divide b by a storing the result in b and the remainder in the word
* pointed to by r.
*/
void ff_big_div(BigInt *b, uint8_t a, uint8_t *r);
/**
* Multiply a by b storing the result in b.
*/
void ff_big_mul(BigInt *b, uint8_t a);
/* Each face is encoded using 9 octrees of 16x16 each. Each level of the
* trees has varying probabilities of being white, grey or black.
* The table below is based on sampling many faces */
enum XFaceColor { XFACE_COLOR_BLACK = 0, XFACE_COLOR_GREY, XFACE_COLOR_WHITE };
/* Data of varying probabilities are encoded by a value in the range 0 - 255.
* The probability of the data determines the range of possible encodings.
* Offset gives the first possible encoding of the range. */
typedef struct {
int range;
int offset;
} ProbRange;
extern const ProbRange ff_xface_probranges_per_level[4][3];
extern const ProbRange ff_xface_probranges_2x2[16];
void ff_xface_generate_face(uint8_t *dst, uint8_t * const src);

@ -0,0 +1,207 @@
/*
* Copyright (c) 1990 James Ashton - Sydney University
* Copyright (c) 2012 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* X-Face decoder, based on libcompface, by James Ashton.
*/
#include "libavutil/pixdesc.h"
#include "avcodec.h"
#include "bytestream.h"
#include "xface.h"
static int pop_integer(BigInt *b, const ProbRange *pranges)
{
uint8_t r;
int i;
/* extract the last byte into r, and shift right b by 8 bits */
ff_big_div(b, 0, &r);
i = 0;
while (r < pranges->offset || r >= pranges->range + pranges->offset) {
pranges++;
i++;
}
ff_big_mul(b, pranges->range);
ff_big_add(b, r - pranges->offset);
return i;
}
static void pop_greys(BigInt *b, char *bitmap, int w, int h)
{
if (w > 3) {
w /= 2;
h /= 2;
pop_greys(b, bitmap, w, h);
pop_greys(b, bitmap + w, w, h);
pop_greys(b, bitmap + XFACE_WIDTH * h, w, h);
pop_greys(b, bitmap + XFACE_WIDTH * h + w, w, h);
} else {
w = pop_integer(b, ff_xface_probranges_2x2);
if (w & 1) bitmap[0] = 1;
if (w & 2) bitmap[1] = 1;
if (w & 4) bitmap[XFACE_WIDTH] = 1;
if (w & 8) bitmap[XFACE_WIDTH + 1] = 1;
}
}
static void decode_block(BigInt *b, char *bitmap, int w, int h, int level)
{
switch (pop_integer(b, &ff_xface_probranges_per_level[level][0])) {
case XFACE_COLOR_WHITE:
return;
case XFACE_COLOR_BLACK:
pop_greys(b, bitmap, w, h);
return;
default:
w /= 2;
h /= 2;
level++;
decode_block(b, bitmap, w, h, level);
decode_block(b, bitmap + w, w, h, level);
decode_block(b, bitmap + h * XFACE_WIDTH, w, h, level);
decode_block(b, bitmap + w + h * XFACE_WIDTH, w, h, level);
return;
}
}
typedef struct XFaceContext {
AVFrame frame;
uint8_t bitmap[XFACE_PIXELS]; ///< image used internally for decoding
} XFaceContext;
static av_cold int xface_decode_init(AVCodecContext *avctx)
{
XFaceContext *xface = avctx->priv_data;
avcodec_get_frame_defaults(&xface->frame);
if (avctx->width || avctx->height) {
if (avctx->width != XFACE_WIDTH || avctx->height != XFACE_HEIGHT) {
av_log(avctx, AV_LOG_ERROR,
"Size value %dx%d not supported, only accepts a size of %dx%d\n",
avctx->width, avctx->height, XFACE_WIDTH, XFACE_HEIGHT);
return AVERROR(EINVAL);
}
}
avctx->width = XFACE_WIDTH;
avctx->height = XFACE_HEIGHT;
avctx->pix_fmt = AV_PIX_FMT_MONOWHITE;
return 0;
}
static av_cold int xface_decode_close(AVCodecContext *avctx)
{
XFaceContext *xface = avctx->priv_data;
if (xface->frame.data[0])
avctx->release_buffer(avctx, &xface->frame);
return 0;
}
static int xface_decode_frame(AVCodecContext *avctx,
void *data, int *data_size,
AVPacket *avpkt)
{
XFaceContext *xface = avctx->priv_data;
int ret, i, j, k;
uint8_t byte;
BigInt b = {0};
char *buf;
int64_t c;
if (xface->frame.data[0])
avctx->release_buffer(avctx, &xface->frame);
xface->frame.data[0] = NULL;
if ((ret = avctx->get_buffer(avctx, &xface->frame)) < 0)
return ret;
xface->frame.reference = 0;
for (i = 0, k = 0; avpkt->data[i] && i < avpkt->size; i++) {
c = avpkt->data[i];
/* ignore invalid digits */
if (c < XFACE_FIRST_PRINT || c > XFACE_LAST_PRINT)
continue;
if (++k > XFACE_MAX_DIGITS) {
av_log(avctx, AV_LOG_WARNING,
"Buffer is longer than expected, truncating at byte %d\n", i);
break;
}
ff_big_mul(&b, XFACE_PRINTS);
ff_big_add(&b, c - XFACE_FIRST_PRINT);
}
/* decode image and put it in bitmap */
memset(xface->bitmap, 0, XFACE_PIXELS);
buf = xface->bitmap;
decode_block(&b, buf, 16, 16, 0);
decode_block(&b, buf + 16, 16, 16, 0);
decode_block(&b, buf + 32, 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 16, 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 16 + 16, 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 16 + 32, 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 32 , 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 32 + 16, 16, 16, 0);
decode_block(&b, buf + XFACE_WIDTH * 32 + 32, 16, 16, 0);
ff_xface_generate_face(xface->bitmap, xface->bitmap);
/* convert image from 1=black 0=white bitmap to MONOWHITE */
buf = xface->frame.data[0];
for (i = 0, j = 0, k = 0, byte = 0; i < XFACE_PIXELS; i++) {
byte += xface->bitmap[i];
if (k == 7) {
buf[j++] = byte;
byte = k = 0;
} else {
k++;
byte <<= 1;
}
if (j == XFACE_WIDTH/8) {
j = 0;
buf += xface->frame.linesize[0];
}
}
*data_size = sizeof(AVFrame);
*(AVFrame*)data = xface->frame;
return avpkt->size;
}
AVCodec ff_xface_decoder = {
.name = "xface",
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_XFACE,
.priv_data_size = sizeof(XFaceContext),
.init = xface_decode_init,
.close = xface_decode_close,
.decode = xface_decode_frame,
.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE },
.long_name = NULL_IF_CONFIG_SMALL("X-face image"),
};

@ -0,0 +1,237 @@
/*
* Copyright (c) 1990 James Ashton - Sydney University
* Copyright (c) 2012 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* X-Face encoder, based on libcompface, by James Ashton.
*/
#include "xface.h"
#include "avcodec.h"
#include "internal.h"
typedef struct XFaceContext {
AVClass *class;
uint8_t bitmap[XFACE_PIXELS]; ///< image used internally for decoding
int max_line_len; ///< max line length for compressed data
int set_header; ///< set X-Face header in the output
} XFaceContext;
static int all_same(char *bitmap, int w, int h)
{
char val, *row;
int x;
val = *bitmap;
while (h--) {
row = bitmap;
x = w;
while (x--)
if (*(row++) != val)
return 0;
bitmap += XFACE_WIDTH;
}
return 1;
}
static int all_black(char *bitmap, int w, int h)
{
if (w > 3) {
w /= 2;
h /= 2;
return (all_black(bitmap, w, h) && all_black(bitmap + w, w, h) &&
all_black(bitmap + XFACE_WIDTH * h, w, h) &&
all_black(bitmap + XFACE_WIDTH * h + w, w, h));
} else {
/* at least one pixel in the 2x2 grid is non-zero */
return *bitmap || *(bitmap + 1) ||
*(bitmap + XFACE_WIDTH) || *(bitmap + XFACE_WIDTH + 1);
}
}
static int all_white(char *bitmap, int w, int h)
{
return *bitmap == 0 && all_same(bitmap, w, h);
}
typedef struct {
const ProbRange *prob_ranges[XFACE_PIXELS*2];
int prob_ranges_idx;
} ProbRangesQueue;
static inline int pq_push(ProbRangesQueue *pq, const ProbRange *p)
{
if (pq->prob_ranges_idx >= XFACE_PIXELS * 2 - 1)
return -1;
pq->prob_ranges[pq->prob_ranges_idx++] = p;
return 0;
}
static void push_greys(ProbRangesQueue *pq, char *bitmap, int w, int h)
{
if (w > 3) {
w /= 2;
h /= 2;
push_greys(pq, bitmap, w, h);
push_greys(pq, bitmap + w, w, h);
push_greys(pq, bitmap + XFACE_WIDTH * h, w, h);
push_greys(pq, bitmap + XFACE_WIDTH * h + w, w, h);
} else {
const ProbRange *p = ff_xface_probranges_2x2 +
*bitmap +
2 * *(bitmap + 1) +
4 * *(bitmap + XFACE_WIDTH) +
8 * *(bitmap + XFACE_WIDTH + 1);
pq_push(pq, p);
}
}
static void encode_block(char *bitmap, int w, int h, int level, ProbRangesQueue *pq)
{
if (all_white(bitmap, w, h)) {
pq_push(pq, &ff_xface_probranges_per_level[level][XFACE_COLOR_WHITE]);
} else if (all_black(bitmap, w, h)) {
pq_push(pq, &ff_xface_probranges_per_level[level][XFACE_COLOR_BLACK]);
push_greys(pq, bitmap, w, h);
} else {
pq_push(pq, &ff_xface_probranges_per_level[level][XFACE_COLOR_GREY]);
w /= 2;
h /= 2;
level++;
encode_block(bitmap, w, h, level, pq);
encode_block(bitmap + w, w, h, level, pq);
encode_block(bitmap + h * XFACE_WIDTH, w, h, level, pq);
encode_block(bitmap + w + h * XFACE_WIDTH, w, h, level, pq);
}
}
static av_cold int xface_encode_init(AVCodecContext *avctx)
{
avctx->coded_frame = avcodec_alloc_frame();
if (!avctx->coded_frame)
return AVERROR(ENOMEM);
avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
return 0;
}
static void push_integer(BigInt *b, const ProbRange *prange)
{
uint8_t r;
ff_big_div(b, prange->range, &r);
ff_big_mul(b, 0);
ff_big_add(b, r + prange->offset);
}
static int xface_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *frame, int *got_packet)
{
XFaceContext *xface = avctx->priv_data;
ProbRangesQueue pq = {{ 0 }, 0};
uint8_t bitmap_copy[XFACE_PIXELS];
BigInt b = {0};
int i, j, k, ret = 0;
const uint8_t *buf;
uint8_t *p;
char intbuf[XFACE_MAX_DIGITS];
if (avctx->width || avctx->height) {
if (avctx->width != XFACE_WIDTH || avctx->height != XFACE_HEIGHT) {
av_log(avctx, AV_LOG_ERROR,
"Size value %dx%d not supported, only accepts a size of %dx%d\n",
avctx->width, avctx->height, XFACE_WIDTH, XFACE_HEIGHT);
return AVERROR(EINVAL);
}
}
avctx->width = XFACE_WIDTH;
avctx->height = XFACE_HEIGHT;
/* convert image from MONOWHITE to 1=black 0=white bitmap */
buf = frame->data[0];
for (i = 0, j = 0; i < XFACE_PIXELS; ) {
for (k = 0; k < 8; k++)
xface->bitmap[i++] = (buf[j]>>(7-k))&1;
if (++j == XFACE_WIDTH/8) {
buf += frame->linesize[0];
j = 0;
}
}
/* create a copy of bitmap */
memcpy(bitmap_copy, xface->bitmap, XFACE_PIXELS);
ff_xface_generate_face(xface->bitmap, bitmap_copy);
encode_block(xface->bitmap, 16, 16, 0, &pq);
encode_block(xface->bitmap + 16, 16, 16, 0, &pq);
encode_block(xface->bitmap + 32, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 16, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 16 + 16, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 16 + 32, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 32, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 32 + 16, 16, 16, 0, &pq);
encode_block(xface->bitmap + XFACE_WIDTH * 32 + 32, 16, 16, 0, &pq);
while (pq.prob_ranges_idx > 0)
push_integer(&b, pq.prob_ranges[--pq.prob_ranges_idx]);
/* write the inverted big integer in b to intbuf */
i = 0;
while (b.nb_words) {
uint8_t r;
ff_big_div(&b, XFACE_PRINTS, &r);
intbuf[i++] = r + XFACE_FIRST_PRINT;
}
if ((ret = ff_alloc_packet2(avctx, pkt, i+2)) < 0)
return ret;
/* revert the number, and close the buffer */
p = pkt->data;
while (--i >= 0)
*(p++) = intbuf[i];
*(p++) = '\n';
*(p++) = 0;
pkt->flags |= AV_PKT_FLAG_KEY;
*got_packet = 1;
return 0;
}
static av_cold int xface_encode_close(AVCodecContext *avctx)
{
av_freep(&avctx->coded_frame);
return 0;
}
AVCodec ff_xface_encoder = {
.name = "xface",
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_XFACE,
.priv_data_size = sizeof(XFaceContext),
.init = xface_encode_init,
.close = xface_encode_close,
.encode2 = xface_encode_frame,
.pix_fmts = (const enum PixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE },
.long_name = NULL_IF_CONFIG_SMALL("X-face image"),
};

@ -74,6 +74,7 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_PICTOR , "pic"},
{ AV_CODEC_ID_V210X , "yuv10"},
{ AV_CODEC_ID_XBM , "xbm"},
{ AV_CODEC_ID_XFACE , "xface"},
{ AV_CODEC_ID_XWD , "xwd"},
{ AV_CODEC_ID_NONE , NULL}
};

@ -152,7 +152,7 @@ AVOutputFormat ff_image2_muxer = {
.long_name = NULL_IF_CONFIG_SMALL("image2 sequence"),
.extensions = "bmp,dpx,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png,"
"ppm,sgi,tga,tif,tiff,jp2,j2c,xwd,sun,ras,rs,im1,im8,im24,"
"sunras,xbm",
"sunras,xbm,xface",
.priv_data_size = sizeof(VideoMuxData),
.video_codec = AV_CODEC_ID_MJPEG,
.write_header = write_header,

@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 54
#define LIBAVFORMAT_VERSION_MINOR 32
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_MICRO 101
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \

Loading…
Cancel
Save