From: Stefano Sabatini <stefa...@gmail.com> Based on libcompface code by James Ashton <james.ash...@anu.edu.au>. Relicensed to LGPL with the author's consent.
Signed-off-by: Vittorio Giovara <vittorio.giov...@gmail.com> --- Changelog | 1 + doc/general.texi | 2 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/version.h | 4 +- libavcodec/xface.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/xface.h | 106 +++++++++++++ libavcodec/xfacedec.c | 195 +++++++++++++++++++++++ libavcodec/xfaceenc.c | 244 ++++++++++++++++++++++++++++ libavformat/img2.c | 1 + libavformat/img2enc.c | 2 +- 13 files changed, 973 insertions(+), 3 deletions(-) create mode 100644 libavcodec/xface.c create mode 100644 libavcodec/xface.h create mode 100644 libavcodec/xfacedec.c create mode 100644 libavcodec/xfaceenc.c diff --git a/Changelog b/Changelog index 279c0d8..b68d22f 100644 --- a/Changelog +++ b/Changelog @@ -4,6 +4,7 @@ releases are sorted from youngest to oldest. version <next>: - compand audio filter - shuffleplanes filter +- X-Face image encoder and decoder version 10: diff --git a/doc/general.texi b/doc/general.texi index 8c0cb1b..79510e1 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -467,6 +467,8 @@ following image formats are supported: @tab WebP image format, encoding supported through external library libwebp @item XBM @tab X @tab @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 diff --git a/libavcodec/Makefile b/libavcodec/Makefile index bd93a6f..e412de9 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -403,6 +403,8 @@ OBJS-$(CONFIG_XAN_DPCM_DECODER) += dpcm.o OBJS-$(CONFIG_XAN_WC3_DECODER) += xan.o OBJS-$(CONFIG_XAN_WC4_DECODER) += xxan.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 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ed6d7ff..13f83c4 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -269,6 +269,7 @@ void avcodec_register_all(void) REGISTER_DECODER(XAN_WC3, xan_wc3); REGISTER_DECODER(XAN_WC4, xan_wc4); REGISTER_ENCODER(XBM, xbm); + REGISTER_ENCDEC (XFACE, xface); REGISTER_DECODER(XL, xl); REGISTER_ENCDEC (XWD, xwd); REGISTER_DECODER(YOP, yop); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 7beb277..20e270a 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -284,6 +284,7 @@ enum AVCodecID { AV_CODEC_ID_HNM4_VIDEO, AV_CODEC_ID_HEVC, AV_CODEC_ID_FIC, + AV_CODEC_ID_XFACE, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 1270323..5b68512 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1259,6 +1259,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Mirillis FIC"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_XFACE, + .type = AVMEDIA_TYPE_VIDEO, + .name = "xface", + .long_name = NULL_IF_CONFIG_SMALL("X-Face image"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { diff --git a/libavcodec/version.h b/libavcodec/version.h index 5ab49d5..d2f80ad 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,8 +29,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 55 -#define LIBAVCODEC_VERSION_MINOR 34 -#define LIBAVCODEC_VERSION_MICRO 1 +#define LIBAVCODEC_VERSION_MINOR 35 +#define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavcodec/xface.c b/libavcodec/xface.c new file mode 100644 index 0000000..9038f5f --- /dev/null +++ b/libavcodec/xface.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 1990 James Ashton - Sydney University + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; 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 less 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; + } + } + } +} diff --git a/libavcodec/xface.h b/libavcodec/xface.h new file mode 100644 index 0000000..d65c21d --- /dev/null +++ b/libavcodec/xface.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1990 James Ashton - Sydney University + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; 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); diff --git a/libavcodec/xfacedec.c b/libavcodec/xfacedec.c new file mode 100644 index 0000000..4643148 --- /dev/null +++ b/libavcodec/xfacedec.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 1990 James Ashton - Sydney University + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; 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 "internal.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 { + uint8_t bitmap[XFACE_PIXELS]; ///< image used internally for decoding +} XFaceContext; + +static av_cold int xface_decode_init(AVCodecContext *avctx) +{ + 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 int xface_decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + XFaceContext *xface = avctx->priv_data; + int ret, i, j, k; + uint8_t byte; + BigInt b = { 0 }; + char *buf; + int64_t c; + AVFrame *frame = data; + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + 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 = 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 += frame->linesize[0]; + } + } + + *got_frame = 1; + + return avpkt->size; +} + +AVCodec ff_xface_decoder = { + .name = "xface", + .long_name = NULL_IF_CONFIG_SMALL("X-Face image"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_XFACE, + .priv_data_size = sizeof(XFaceContext), + .init = xface_decode_init, + .decode = xface_decode_frame, + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE + }, +}; diff --git a/libavcodec/xfaceenc.c b/libavcodec/xfaceenc.c new file mode 100644 index 0000000..125a700 --- /dev/null +++ b/libavcodec/xfaceenc.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1990 James Ashton - Sydney University + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; 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 <string.h> + +#include "libavutil/internal.h" + +#include "avcodec.h" +#include "internal.h" +#include "xface.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 = av_frame_alloc(); + 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]; + i = j = 0; + do { + 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; + } + } while (i < XFACE_PIXELS); + + /* 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_packet(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", + .long_name = NULL_IF_CONFIG_SMALL("X-Face image"), + .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 + }, +}; diff --git a/libavformat/img2.c b/libavformat/img2.c index 493df6d..e10d684 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -70,6 +70,7 @@ static const IdStrMap img_tags[] = { { AV_CODEC_ID_PICTOR, "pic" }, { AV_CODEC_ID_WEBP, "webp" }, { AV_CODEC_ID_XBM, "xbm" }, + { AV_CODEC_ID_XFACE, "xface" }, { AV_CODEC_ID_XWD, "xwd" }, { AV_CODEC_ID_NONE, NULL } }; diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index 4cc5c3f..552bf5c 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -148,7 +148,7 @@ AVOutputFormat ff_image2_muxer = { .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), .extensions = "bmp,dpx,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," "ppm,sgi,tga,tif,tiff,jp2,xwd,sun,ras,rs,im1,im8,im24," - "sunras,webp,xbm", + "sunras,webp,xbm,xface", .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, -- 1.8.3.4 (Apple Git-47) _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel