[FFmpeg-devel] [PATCH 1/3] avcodec/qmagedec: Quram Qmage decoder
Peter Ross
pross at xvid.org
Thu Nov 21 07:45:20 EET 2024
---
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/codec_desc.c | 7 +
libavcodec/codec_id.h | 1 +
libavcodec/qmagedata.h | 133 +++++++
libavcodec/qmagedec.c | 836 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 979 insertions(+)
create mode 100644 libavcodec/qmagedata.h
create mode 100644 libavcodec/qmagedec.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a6e0e0b55e..eb724eab21 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -634,6 +634,7 @@ OBJS-$(CONFIG_QCELP_DECODER) += qcelpdec.o \
OBJS-$(CONFIG_QDM2_DECODER) += qdm2.o
OBJS-$(CONFIG_QDMC_DECODER) += qdmc.o
OBJS-$(CONFIG_QDRAW_DECODER) += qdrw.o
+OBJS-$(CONFIG_QMAGE_DECODER) += qmagedec.o
OBJS-$(CONFIG_QOA_DECODER) += qoadec.o
OBJS-$(CONFIG_QOI_DECODER) += qoidec.o
OBJS-$(CONFIG_QOI_ENCODER) += qoienc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 0b559dfc58..9ae639859c 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -271,6 +271,7 @@ extern const FFCodec ff_prosumer_decoder;
extern const FFCodec ff_psd_decoder;
extern const FFCodec ff_ptx_decoder;
extern const FFCodec ff_qdraw_decoder;
+extern const FFCodec ff_qmage_decoder;
extern const FFCodec ff_qoi_encoder;
extern const FFCodec ff_qoi_decoder;
extern const FFCodec ff_qpeg_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index aeac75a6c5..4d52fed5f5 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1977,6 +1977,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
.long_name = NULL_IF_CONFIG_SMALL("RealVideo 6.0"),
.props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER,
},
+ {
+ .id = AV_CODEC_ID_QMAGE,
+ .type = AVMEDIA_TYPE_VIDEO,
+ .name = "qmage",
+ .long_name = NULL_IF_CONFIG_SMALL("Quram Qmage"),
+ .props = AV_CODEC_PROP_LOSSLESS,
+ },
/* various PCM "codecs" */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 6bfaa02601..930efa3454 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -328,6 +328,7 @@ enum AVCodecID {
AV_CODEC_ID_LEAD,
AV_CODEC_ID_DNXUC,
AV_CODEC_ID_RV60,
+ AV_CODEC_ID_QMAGE,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/qmagedata.h b/libavcodec/qmagedata.h
new file mode 100644
index 0000000000..21fc8789f2
--- /dev/null
+++ b/libavcodec/qmagedata.h
@@ -0,0 +1,133 @@
+/*
+ * Quram Qmage image format decoder
+ *
+ * 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
+ */
+
+#include <stdint.h>
+
+static const struct { int8_t x, y; } qmage_dir[3] = {
+ [0] = {-1, 0},
+ [1] = {0, -1},
+ [2] = {-1,-1},
+};
+
+static const uint16_t qmage_ori_delta[2][256] = {
+ {
+ 0xffe0, 0x0020, 0xffff, 0xf7e0, 0xf800, 0xffdf, 0x0001, 0x0800,
+ 0x0021, 0x0820, 0x0841, 0xf7bf, 0xf7df, 0x0821, 0xf7c0, 0xffbf,
+ 0x0041, 0xffc0, 0x0861, 0x0840, 0x001f, 0x0862, 0xffe1, 0xf79f,
+ 0x0040, 0xf7ff, 0xf820, 0x0842, 0x1082, 0x07e0, 0xf79e, 0x0801,
+ 0x1061, 0x1062, 0x07ff, 0xef7e, 0xef9f, 0xf801, 0xf7be, 0xefbf,
+ 0x0022, 0x0882, 0xefe0, 0xefc0, 0x07df, 0x1041, 0x081f, 0x10a3,
+ 0x0042, 0x1081, 0xef9e, 0xf7e1, 0xf821, 0xf77e, 0xff9f, 0x10a2,
+ 0x1040, 0xffbe, 0x1083, 0x1020, 0xef5d, 0xef7f, 0xffde, 0xef5e,
+ 0x0002, 0x0061, 0xf7a0, 0x18c3, 0xef7d, 0x0822, 0xff9e, 0xf000,
+ 0x0860, 0xe73d, 0xefa0, 0x0881, 0xefdf, 0x07bf, 0xf77f, 0x0062,
+ 0xfffe, 0x18a3, 0x10c3, 0x0883, 0x18e4, 0x1060, 0x0863, 0x1000,
+ 0x18a2, 0xe71c, 0xf7de, 0x1021, 0x1042, 0x18c4, 0xef3d, 0x18e3,
+ 0xf841, 0xe75d, 0x1881, 0xe73c, 0xe7c0, 0x18c2, 0xe75e, 0xf77d,
+ 0x10a4, 0x07c0, 0xf81f, 0x10c4, 0xff7e, 0x003f, 0xf840, 0xe79f,
+ 0x2104, 0xffa0, 0xffc1, 0x18a1, 0xe77f, 0xefbe, 0xf79d, 0x1882,
+ 0x10a1, 0xe71d, 0x1063, 0xe7a0, 0xe7e0, 0xf7c1, 0x1861, 0x08a3,
+ 0x1840, 0xe73e, 0x083f, 0xf001, 0xdefc, 0xf75e, 0x0060, 0x2125,
+ 0x07e1, 0x0fff, 0xef5f, 0xe77e, 0xefe1, 0xe6fc, 0x10c2, 0x08a2,
+ 0x0023, 0x1860, 0xef3c, 0xef3e, 0x0043, 0xf020, 0xf75d, 0x079f,
+ 0x101f, 0xdedb, 0xef5c, 0xffe2, 0xf822, 0x20e4, 0x001e, 0x0843,
+ 0xe7bf, 0x20e3, 0x0082, 0xdefb, 0xef9d, 0x1904, 0xff7f, 0x1820,
+ 0xe75f, 0x0063, 0xef80, 0x2105, 0x1905, 0xdefd, 0x2124, 0xdf1c,
+ 0x2103, 0x1080, 0xdf1d, 0xe6fb, 0x07be, 0x2945, 0xdedc, 0xf021,
+ 0x0fe0, 0xe71b, 0xefff, 0xf842, 0x20c2, 0x079e, 0xf802, 0x0fdf,
+ 0x20e2, 0x18e2, 0xe780, 0x07de, 0x1880, 0xdfa0, 0x20c1, 0xffbd,
+ 0x08a4, 0x10e4, 0x18c1, 0xdfc0, 0x103f, 0x0003, 0xdeba, 0xff7d,
+ 0x1841, 0xe800, 0x20a1, 0xefc1, 0xff9d, 0xf780, 0x18e5, 0x10e3,
+ 0x1084, 0x0081, 0x2966, 0xffdd, 0x08c3, 0x18a4, 0xdeda, 0xff5e,
+ 0xdf7f, 0x1883, 0xdebb, 0x2145, 0xd6bb, 0xf7bd, 0x20c3, 0x1903,
+ 0x077e, 0x0884, 0xf861, 0xe71e, 0x1001, 0xe75c, 0x0083, 0xd6ba,
+ 0x07fe, 0x2102, 0xdf80, 0x2081, 0x0880, 0xdf3e, 0x2946, 0xe6fd,
+ 0x2146, 0xef1c, 0xdf9f, 0xdf1e, 0xe73f, 0xe79e, 0x20a2, 0xd69a,
+ },
+ {
+ 0xffe0, 0x0020, 0xffff, 0x0001, 0xf800, 0x0800, 0xf7df, 0xffdf,
+ 0xf7e0, 0x0021, 0xf7bf, 0x0821, 0x0820, 0x0841, 0xf7ff, 0x0801,
+ 0xffc0, 0x07e0, 0xf820, 0xf801, 0x07ff, 0xf7c0, 0xffe1, 0x0040,
+ 0x001f, 0xffbf, 0xf79f, 0x0840, 0xffff, 0x0041, 0x0861, 0xef7e,
+ 0x1082, 0xef9e, 0xf81f, 0xf7a0, 0x0001, 0xe73d, 0xef9f, 0x1062,
+ 0x0860, 0x1061, 0x18c3, 0x07df, 0xf7be, 0xef5e, 0xefbf, 0xf79e,
+ 0xf7e1, 0xf821, 0x10a2, 0xef7f, 0x0842, 0x07e1, 0xdefc, 0x1041,
+ 0x081f, 0x0862, 0x0002, 0x1081, 0x2104, 0xf81f, 0xefc0, 0x07e1,
+ 0xfffe, 0xffbe, 0xe75d, 0x2945, 0xffa0, 0xf780, 0x1040, 0xd6bb,
+ 0x0042, 0x18a3, 0x18e3, 0x0880, 0x0822, 0xff9f, 0x0022, 0xffc1,
+ 0x003f, 0xffde, 0xe71d, 0x3186, 0xef5f, 0xf7de, 0xce7a, 0x0060,
+ 0x10a1, 0xe75e, 0xf77e, 0xdedc, 0x39c7, 0xf77f, 0xc639, 0x18a2,
+ 0xefe0, 0x2124, 0x1080, 0x20e4, 0x0061, 0x1020, 0x1000, 0xefa0,
+ 0xff9e, 0x07c0, 0xdf1c, 0xbdf8, 0x1060, 0xf000, 0x4208, 0xd69b,
+ 0x0881, 0x5acb, 0x2965, 0x0882, 0xef80, 0xf840, 0xe77f, 0xa535,
+ 0xe73e, 0xce5a, 0x21ae, 0xf760, 0x1881, 0x4a49, 0xefbe, 0xb5b7,
+ 0x0062, 0xf7c1, 0x083f, 0x2925, 0xe73f, 0x4a4a, 0xef3f, 0x08a0,
+ 0xe75f, 0x1042, 0xad76, 0x18c2, 0x3126, 0xf7fe, 0x528a, 0xc619,
+ 0x31a6, 0xd6db, 0xff7e, 0x10c1, 0xe77e, 0xceda, 0x18c1, 0xb5b6,
+ 0x07bf, 0xf83f, 0x630c, 0xde52, 0x39e7, 0xefdf, 0xef5d, 0x1861,
+ 0x10a0, 0xe79f, 0x9cf4, 0xce9a, 0xff7f, 0x18a1, 0x1021, 0x0802,
+ 0xef7d, 0x1882, 0x3166, 0x0003, 0xf841, 0xf7bd, 0xe71f, 0xdf7f,
+ 0x10a3, 0x07c1, 0x18e1, 0x94b3, 0x2081, 0xbdd8, 0x6b4d, 0xef60,
+ 0x1083, 0xdf1d, 0xff7d, 0x0863, 0xf79d, 0xdf3e, 0xf77d, 0x4228,
+ 0xad56, 0xf7dd, 0x20e3, 0x52aa, 0xb597, 0x3967, 0xc699, 0x20c2,
+ 0x8c72, 0xff80, 0x001e, 0x39a7, 0xe6ff, 0xff5f, 0xc659, 0x0082,
+ 0x1901, 0x1063, 0x4a69, 0xef9d, 0x0843, 0xd6dc, 0xfffd, 0x738e,
+ 0x07fe, 0x8431, 0xffe2, 0xe7a0, 0xef40, 0x7bf0, 0xe71c, 0xff9d,
+ 0xef3d, 0xefff, 0x2924, 0xa515, 0xd73e, 0x8410, 0x28c2, 0x7bcf,
+ 0x1043, 0xf75e, 0x41e8, 0x10c3, 0xbe18, 0x5aeb, 0xd6dd, 0xdf5f,
+ 0x9cd4, 0x0883, 0x20a2, 0xf802, 0x632c, 0xe780, 0x0083, 0xdf5e,
+ 0xefbd, 0xff5d, 0x0823, 0x1880, 0x1022, 0xdefd, 0x18e4, 0xefde,
+ }
+};
+
+static const uint16_t qmage_diff[256] = {
+ 0x0001, 0x0003, 0x0100, 0x0002, 0x0008, 0x0007, 0x0006, 0x0300,
+ 0x0010, 0x0004, 0x0200, 0x0009, 0x0040, 0x0018, 0x0005, 0x0020,
+ 0x000c, 0x000e, 0x000f, 0x000a, 0x00c0, 0x0800, 0x0700, 0x0101,
+ 0x0400, 0x000b, 0x0030, 0x0011, 0x0080, 0x0600, 0x000d, 0x0012,
+ 0x001c, 0x0500, 0x001b, 0x001e, 0x0014, 0x001a, 0x0028, 0x0038,
+ 0x1000, 0x001f, 0x0019, 0x0016, 0x0060, 0x2000, 0x0013, 0x001d,
+ 0x0103, 0x0024, 0x0017, 0x0015, 0x0102, 0x01c0, 0x0f00, 0x003c,
+ 0x0301, 0x0c00, 0x1800, 0x0048, 0x0021, 0x0034, 0x0e00, 0x0202,
+ 0x002c, 0x0070, 0x0a00, 0x0303, 0x0036, 0x0201, 0x003f, 0x0d00,
+ 0x0180, 0x003e, 0x3000, 0x0900, 0x0078, 0x0022, 0x0050, 0x003a,
+ 0x0041, 0x0107, 0x0033, 0x0106, 0x0026, 0x002a, 0x00a0, 0x0023,
+ 0x0029, 0x0088, 0x0044, 0x003d, 0x00e0, 0x0032, 0x002e, 0x0039,
+ 0x0031, 0x002d, 0x00f0, 0x0140, 0x0b00, 0x003b, 0x0058, 0x4000,
+ 0x0037, 0x0035, 0x0068, 0x0302, 0x007c, 0x002f, 0x0027, 0x0064,
+ 0x0090, 0x0074, 0x0203, 0x0104, 0x006c, 0x1100, 0x03c0, 0x00ff,
+ 0x0025, 0xf000, 0x1f00, 0x0701, 0x0042, 0x007f, 0x002b, 0x0105,
+ 0x0054, 0x1c00, 0x004c, 0x0801, 0x0043, 0x6000, 0x005c, 0x007e,
+ 0x00e8, 0x0108, 0x00f8, 0xe000, 0x0206, 0x1e00, 0x0380, 0x0061,
+ 0x007a, 0x004e, 0x0601, 0x1001, 0x00c8, 0x8000, 0x1d00, 0x00d0,
+ 0x0072, 0x0049, 0x1600, 0x1a00, 0x0046, 0x7000, 0x010f, 0x0110,
+ 0x0076, 0x1200, 0x1400, 0x0404, 0x0606, 0x010e, 0x00fc, 0x1700,
+ 0x006e, 0x00fe, 0x1300, 0x0062, 0x0066, 0xc000, 0x0204, 0x0306,
+ 0x0063, 0x0707, 0x0280, 0x0602, 0x0055, 0x0047, 0x006a, 0x010c,
+ 0x0052, 0x0501, 0x00d8, 0x0307, 0x0073, 0x0109, 0x0808, 0x0401,
+ 0x004a, 0x2020, 0x005a, 0x0702, 0x00b0, 0x0045, 0x0207, 0x0304,
+ 0x0402, 0x005e, 0x010a, 0x0079, 0x3800, 0x00f4, 0x1500, 0x01e0,
+ 0x1b00, 0x0071, 0x1010, 0x00c1, 0x00e4, 0x0502, 0x0056, 0x007d,
+ 0x0081, 0x0077, 0x00cc, 0x0703, 0x010d, 0x0205, 0x0340, 0x5000,
+ 0x0082, 0x0067, 0xff00, 0x0120, 0x0069, 0x0098, 0x00c3, 0x1900,
+ 0x0065, 0x007b, 0x0240, 0x0603, 0x00ec, 0x0059, 0x00fa, 0x0403,
+ 0x0075, 0x006f, 0x3100, 0x3300, 0x004f, 0x00b8, 0x006d, 0x0208,
+ 0x004d, 0x0111, 0x0051, 0x020e, 0x00dc, 0x00c4, 0x2100, 0x00a8,
+};
diff --git a/libavcodec/qmagedec.c b/libavcodec/qmagedec.c
new file mode 100644
index 0000000000..8c3d6cbc60
--- /dev/null
+++ b/libavcodec/qmagedec.c
@@ -0,0 +1,836 @@
+/*
+ * Quram Qmage image format decoder
+ * Copyright (c) 2024 Peter Ross
+ *
+ * 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
+ */
+
+#include <inttypes.h>
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "copy_block.h"
+#include "decode.h"
+#include "get_bits.h"
+#include "libavutil/mem.h"
+#include "qmagedata.h"
+
+#define QMAGE_MAGIC 0x514d
+#define QVERSION_1_43_LESS 0xb
+#define QCODEC_V16_SHORT_INDEX 0
+#define QCODEC_W2_PASS 1
+
+typedef struct {
+ AVFrame * last_frame;
+
+ int qversion;
+
+ int raw_type;
+ int transparency;
+
+ int qp;
+ int not_comp;
+ int use_chroma_key;
+ int mode;
+
+ int encoder_mode;
+ int is_dynamic_table;
+ int alpha_depth;
+ int depth;
+ int use_extra_exception;
+
+ int width;
+ int height;
+
+ int near_lossless;
+
+ int android_support;
+ int is_gray_type;
+ int use_index_color;
+ int pre_multiplied;
+ int not_alpha_comp;
+ int is_opaque;
+ int nine_patched;
+
+ int alpha_position;
+ int alpha_encoder_mode;
+
+ int total_frame_number;
+ int current_frame_number;
+ int animation_delay_time;
+ int animation_no_repeat;
+
+ int header_size;
+
+ int color_count;
+} Context;
+
+static void dump(AVCodecContext *avctx)
+{
+ const Context * ctx = avctx->priv_data;
+ av_log(avctx, AV_LOG_DEBUG, "qversion: 0x%x\n", ctx->qversion);
+ av_log(avctx, AV_LOG_DEBUG, "raw_type: %d\n", ctx->raw_type);
+ av_log(avctx, AV_LOG_DEBUG, "transparency: %d\n", ctx->transparency);
+ av_log(avctx, AV_LOG_DEBUG, "qp: %d\n", ctx->qp);
+ av_log(avctx, AV_LOG_DEBUG, "not_comp: %d\n", ctx->not_comp);
+ av_log(avctx, AV_LOG_DEBUG, "use_chroma_key: %d\n", ctx->use_chroma_key);
+ av_log(avctx, AV_LOG_DEBUG, "mode: %d\n", ctx->mode);
+ av_log(avctx, AV_LOG_DEBUG, "encoder_mode: %d\n", ctx->encoder_mode);
+ av_log(avctx, AV_LOG_DEBUG, "is_dynamic_table: %d\n", ctx->is_dynamic_table);
+ av_log(avctx, AV_LOG_DEBUG, "alpha_depth: %d\n", ctx->alpha_depth);
+ av_log(avctx, AV_LOG_DEBUG, "depth: %d\n", ctx->depth);
+ av_log(avctx, AV_LOG_DEBUG, "use_extra_exception: %d\n", ctx->use_extra_exception);
+ av_log(avctx, AV_LOG_DEBUG, "width: %d\n", ctx->width);
+ av_log(avctx, AV_LOG_DEBUG, "height: %d\n", ctx->height);
+ av_log(avctx, AV_LOG_DEBUG, "near_lossless: %d\n", ctx->near_lossless);
+ av_log(avctx, AV_LOG_DEBUG, "android_support: %d\n", ctx->android_support);
+ av_log(avctx, AV_LOG_DEBUG, "is_gray_type: %d\n", ctx->is_gray_type);
+ av_log(avctx, AV_LOG_DEBUG, "use_index_color: %d\n", ctx->use_index_color);
+ av_log(avctx, AV_LOG_DEBUG, "pre_multiplied: %d\n", ctx->pre_multiplied);
+ av_log(avctx, AV_LOG_DEBUG, "not_alpha_comp: %d\n", ctx->not_alpha_comp);
+ av_log(avctx, AV_LOG_DEBUG, "is_opaque: %d\n", ctx->is_opaque);
+ av_log(avctx, AV_LOG_DEBUG, "nine_patched: %d\n", ctx->nine_patched);
+ av_log(avctx, AV_LOG_DEBUG, "alpha_position: 0x%x\n", ctx->alpha_position);
+ av_log(avctx, AV_LOG_DEBUG, "total_frame_number: %d\n", ctx->total_frame_number);
+ av_log(avctx, AV_LOG_DEBUG, "current_frame_number: %d\n", ctx->current_frame_number);
+ av_log(avctx, AV_LOG_DEBUG, "animation_delay_time: %d\n", ctx->animation_delay_time);
+ av_log(avctx, AV_LOG_DEBUG, "animation_no_repeat: %d\n", ctx->animation_no_repeat);
+ av_log(avctx, AV_LOG_DEBUG, "header_size: %d\n", ctx->header_size);
+ av_log(avctx, AV_LOG_DEBUG, "color_count: %d\n", ctx->color_count);
+}
+
+static int decode_header(AVCodecContext *avctx, AVPacket *avpkt)
+{
+ Context * ctx = avctx->priv_data;
+ GetByteContext gb;
+ int flags4, flags5, flags10, flags11;
+
+ if (avpkt->size < 12)
+ return AVERROR_INVALIDDATA;
+
+ bytestream2_init(&gb, avpkt->data, avpkt->size);
+
+ if (bytestream2_get_be16(&gb) != QMAGE_MAGIC) {
+ av_log(avctx, AV_LOG_ERROR, "bad magic number\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ ctx->qversion = bytestream2_get_byte(&gb);
+
+ ctx->raw_type = bytestream2_get_byte(&gb);
+ switch (ctx->raw_type) {
+ case 0: //RGB565
+ ctx->transparency = 0;
+ break;
+ case 3: //RGBA5658
+ case 6: //RGBA
+ ctx->transparency = 1;
+ break;
+ default:
+ avpriv_request_sample(avctx, "raw_type=%d", ctx->raw_type);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ flags4 = bytestream2_get_byte(&gb);
+ ctx->qp = flags4 & 0x1f;
+ ctx->not_comp = !!(flags4 & 0x20);
+ ctx->use_chroma_key = !!(flags4 & 0x40);
+ ctx->mode = !!(flags4 & 0x80);
+
+ flags5 = bytestream2_get_byte(&gb);
+ if (ctx->qversion == QVERSION_1_43_LESS)
+ ctx->encoder_mode = flags5 & 0x7;
+ else if (ctx->qversion > QVERSION_1_43_LESS)
+ ctx->encoder_mode = flags5 & 0xf;
+ else
+ ctx->encoder_mode = 0;
+ if (ctx->qversion > QVERSION_1_43_LESS)
+ ctx->is_dynamic_table = !!(flags5 & 0x10);
+ else
+ ctx->is_dynamic_table = 0;
+ ctx->alpha_depth = (flags5 & 0x20) ? 2 : 1;
+ ctx->depth = (flags5 & 0x40) ? 2 : 1;
+ ctx->use_extra_exception = !!(flags5 & 0x80);
+
+ ctx->width = bytestream2_get_le16(&gb);
+ ctx->height = bytestream2_get_le16(&gb);
+
+ flags10 = bytestream2_get_byte(&gb);
+ ctx->near_lossless = !!(flags10 & 0x40);
+
+ flags11 = bytestream2_get_byte(&gb);
+ ctx->android_support = !!(flags11 & 0x4);
+ ctx->is_gray_type = !!(flags11 & 0x4);
+ ctx->use_index_color = !!(flags11 & 0x8);
+ ctx->pre_multiplied = !!(flags11 & 0x10);
+ ctx->not_alpha_comp = !!(flags11 & 0x40);
+ ctx->is_opaque = !!(flags11 & 0x20);
+ ctx->nine_patched = !!(flags11 & 0x80);
+
+ if (ctx->qversion == QVERSION_1_43_LESS) {
+ if (ctx->transparency || ctx->mode)
+ ctx->alpha_position = bytestream2_get_le32(&gb);
+ ctx->alpha_encoder_mode = ctx->encoder_mode;
+ } else if (ctx->qversion > QVERSION_1_43_LESS) {
+ int flags14;
+ ctx->alpha_position = bytestream2_get_le16(&gb);
+ flags14 = bytestream2_get_byte(&gb);
+ ctx->alpha_encoder_mode = flags14 & 0xf;
+ bytestream2_skip(&gb, 1);
+ }
+
+ if (ctx->mode) {
+ ctx->total_frame_number = bytestream2_get_le16(&gb);
+ ctx->current_frame_number = bytestream2_get_le16(&gb);
+ ctx->animation_delay_time = bytestream2_get_le16(&gb);
+ ctx->animation_no_repeat = bytestream2_get_byte(&gb);
+ bytestream2_skip(&gb, 1);
+ } else {
+ ctx->total_frame_number = ctx->current_frame_number = 1;
+ }
+
+ if (ctx->qversion > QVERSION_1_43_LESS) {
+ if (!ctx->mode || ctx->current_frame_number <= 1)
+ ctx->alpha_position *= 4;
+ }
+
+ if (ctx->mode) {
+ ctx->header_size = 24;
+ } else {
+ ctx->header_size = ctx->transparency ? 16 : 12;
+ }
+
+ if (ctx->use_index_color) {
+ if (ctx->nine_patched)
+ bytestream2_skip(&gb, 4);
+ ctx->color_count = bytestream2_get_le32(&gb);
+ }
+
+ dump(avctx);
+
+ return 0;
+}
+
+static uint16_t get_pixel(AVCodecContext * avctx, const uint8_t * src, int linesize, int x, int y)
+{
+ if (x >= 0 && x < avctx->width && y >= 0 && y < avctx->height)
+ return AV_RN16A(src + y*linesize + x*2);
+ return 0;
+}
+
+static void decode_pixel_inter(AVCodecContext * avctx, int copy, const uint16_t * ori_delta, GetBitContext * gb1, GetBitContext *gb2, GetByteContext * gb3, uint8_t * dst, const uint8_t * ref, int ref_linesize, int ref_x, int ref_y)
+{
+ if (copy) {
+ AV_WN16A(dst, get_pixel(avctx, ref, ref_linesize, ref_x, ref_y));
+ } else {
+ int nb_bits = get_bits(gb2, 3);
+ if (nb_bits == 7) {
+ AV_WN16A(dst, bytestream2_get_le16(gb3));
+ } else {
+ int idx = get_bits(gb1, nb_bits + 1);
+ uint16_t delta = ori_delta[idx + (2 << nb_bits) - 2];
+ AV_WN16A(dst, get_pixel(avctx, ref, ref_linesize, ref_x, ref_y) + delta);
+ }
+ }
+}
+
+static void copy_edge(uint8_t * dst, int linesize, int width, int height)
+{
+ for (int j = 0; j < height; j++)
+ for (int i = 0; i < width; i++)
+ AV_WN16A(dst + j*linesize + i*2, AV_RN16A(dst + j*linesize - 2));
+}
+
+static int decode_a9ll(AVCodecContext *avctx, uint8_t * data, int size, uint8_t * dst, int dst_linesize)
+{
+ Context * s = avctx->priv_data;
+ GetBitContext gb1, gb2;
+ GetByteContext gb3;
+ int gb1_start, gb3_start, ret;
+ const uint16_t * ori_delta;
+ uint16_t ori_delta_local[512];
+
+ if (size < s->header_size + 8)
+ return AVERROR_INVALIDDATA;
+ gb1_start = AV_RL32(data + s->header_size);
+ gb3_start = AV_RL32(data + s->header_size + 4);
+ if (gb1_start < s->header_size + 8 || gb1_start > size || gb3_start < s->header_size + 8 || gb3_start > size)
+ return AVERROR_INVALIDDATA;
+ if ((ret = init_get_bits8(&gb1, data + s->header_size + 8, size - s->header_size - 8)) < 0)
+ return ret;
+ if ((ret = init_get_bits8(&gb2, data + gb1_start, size - gb1_start)) < 0)
+ return ret;
+ bytestream2_init(&gb3, data + gb3_start, size - gb3_start);
+
+ if (s->is_dynamic_table) {
+ uint8_t sign[512];
+ for (int i = 0; i < 512; i++)
+ sign[i] = bytestream2_get_byte(&gb3);
+ for (int i = 0; i < 512; i++) {
+ int v = bytestream2_get_le16(&gb3);
+ ori_delta_local[i] = sign[i] ? v : -v;
+ }
+ ori_delta = ori_delta_local + 1;
+ } else
+ ori_delta = qmage_ori_delta[s->qversion != QVERSION_1_43_LESS];
+
+ if (s->use_extra_exception) {
+ avpriv_request_sample(avctx, "use_extra_exception");
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (int y = 0; y < avctx->height; y += 4) {
+ for (int x = 0; x < avctx->width; x += 4) {
+ int mode = get_bits(&gb1, 2);
+ if (mode < 3) {
+ int cbp = bytestream2_get_le16(&gb3);
+ int k = 0;
+ for (int j = 0; j < 4; j++) {
+ for (int i = 0; i < 4; i++) {
+ if (x + i < avctx->width && y + j < avctx->height) {
+ decode_pixel_inter(avctx, cbp & (1 << k), ori_delta, &gb1, &gb2, &gb3,
+ dst + (y+j)*dst_linesize + (x+i)*2,
+ dst, dst_linesize,
+ x+i+qmage_dir[mode].x, y+j+qmage_dir[mode].y);
+ k++;
+ }
+ }
+ }
+ } else {
+ if (x > 0)
+ copy_edge(dst + y*dst_linesize + x*2, dst_linesize,
+ FFMIN(avctx->width - x, 4), FFMIN(avctx->height - y, 4));
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void copy_block4x4(uint8_t * dst, int dst_linesize, const uint8_t * ref, int ref_linesize)
+{
+ copy_block8(dst, ref, dst_linesize, ref_linesize, 4);
+}
+
+static void copy_block16x16(uint8_t * dst, int dst_linesize, const uint8_t * ref, int ref_linesize)
+{
+ copy_block16(dst, ref, dst_linesize, ref_linesize, 16);
+ copy_block16(dst + 16, ref + 16, dst_linesize, ref_linesize, 16);
+}
+
+static void decode_pixel(AVCodecContext * avctx, GetBitContext * gb1, GetByteContext * gb2, const uint16_t * ori_delta, uint8_t * dst, const uint8_t * ref, int ref_linesize, int ref_x, int ref_y)
+{
+ int skip = get_bits1(gb1);
+ if (skip) {
+ AV_WN16A(dst, get_pixel(avctx, ref, ref_linesize, ref_x, ref_y));
+ } else {
+ int nb_bits = get_bits(gb1, 3);
+ if (nb_bits == 7) {
+ AV_WN16A(dst, bytestream2_get_le16(gb2));
+ } else {
+ int idx = get_bits(gb1, nb_bits + 1);
+ uint16_t delta = ori_delta[idx + (2 << nb_bits) - 2];
+ AV_WN16A(dst, get_pixel(avctx, ref, ref_linesize, ref_x, ref_y) + delta);
+ }
+ }
+}
+
+static void decode_block3_ani(AVCodecContext *avctx, GetBitContext * gb1, GetByteContext * gb2, int x, int y, uint8_t * dst, int linesize, const uint8_t * ref, int ref_linesize, int mv_x, int mv_y, const uint16_t * ori_delta)
+{
+ Context * s = avctx->priv_data;
+ int mode = get_bits(gb1, 3);
+ if (s->qp == 0 || get_bits1(gb1)) {
+ if (mode < 3) {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ decode_pixel(avctx, gb1, gb2, ori_delta,
+ dst + (y+j)*linesize + (x+i)*2,
+ dst, linesize, x+i+qmage_dir[mode].x, y+j+qmage_dir[mode].y);
+ } else if (mode == 3) {
+ if (x > 0)
+ copy_edge(dst + y*linesize + x*2, linesize, 4, 4);
+ } else if (mode == 4) {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ decode_pixel(avctx, gb1, gb2, ori_delta,
+ dst + (y+j)*linesize + (x+i)*2,
+ ref, ref_linesize, x+i, y+j);
+ } else if (mode == 5) {
+ copy_block4x4(dst + y*linesize + x*2, linesize,
+ ref + y*ref_linesize + x*2, ref_linesize);
+ } else if (mode == 6) {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ decode_pixel(avctx, gb1, gb2, ori_delta,
+ dst + (y+j)*linesize + (x+i)*2,
+ ref, ref_linesize, x+i+mv_x, y+j+mv_y);
+ } else {
+ if (x+mv_x < 0 || x+mv_x+4 > avctx->width ||
+ y+mv_y < 0 || y+mv_y+4 > avctx->height) {
+ av_log(avctx, AV_LOG_WARNING, "offscreen mv");
+ return;
+ }
+ copy_block4x4(dst + y*linesize + x*2, linesize,
+ ref + (y+mv_y)*ref_linesize + (x+mv_x)*2, ref_linesize);
+ }
+ } else {
+ avpriv_request_sample(avctx, "qp");
+ }
+}
+
+static void decode_block2_ani(AVCodecContext *avctx, GetBitContext * gb1, GetByteContext * gb2, int x, int y, uint8_t * dst, int linesize, const uint16_t * ori_delta)
+{
+ Context * s = avctx->priv_data;
+ int mode = get_bits(gb1, 2);
+ if (s->qp == 0 || get_bits1(gb1)) {
+ if (mode < 3) {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ decode_pixel(avctx, gb1, gb2, ori_delta,
+ dst + (y+j)*linesize + (x+i)*2,
+ dst, linesize, x+i+qmage_dir[mode].x, y+j+qmage_dir[mode].y);
+ } else {
+ if (x > 0)
+ copy_edge(dst + y*linesize + x*2, linesize, 4, 4);
+ }
+ } else {
+ avpriv_request_sample(avctx, "qp");
+ }
+}
+
+static int decode_mb_ani(AVCodecContext *avctx, GetBitContext * gb1, GetByteContext * gb2, int x, int y, uint8_t * dst, int linesize, const uint8_t * ref, int ref_linesize, const uint16_t * ori_delta)
+{
+ if (get_bits1(gb1)) {
+ if (get_bits1(gb1)) {
+ copy_block16x16(dst + y*linesize + x*2, linesize, ref + y*ref_linesize + x*2, ref_linesize);
+ } else {
+ int mv_x, mv_y;
+ if (!get_bits1(gb1)) {
+ mv_x = get_bits(gb1, 8) - 0x7f;
+ mv_y = get_bits(gb1, 7) - 0x3f;
+ if (x+mv_x < 0 || x+mv_x+16 > avctx->width ||
+ y+mv_y < 0 || y+mv_y+16 > avctx->height) {
+ av_log(avctx, AV_LOG_WARNING, "offscreen mv");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (get_bits1(gb1)) {
+ copy_block16x16(dst + y*linesize + x*2, linesize,
+ ref + (y+mv_y)*ref_linesize + (x+mv_x)*2, ref_linesize);
+ return 0;
+ }
+ } else {
+ mv_x = mv_y = 0;
+ }
+ for (int j = 0; j < 16; j += 4)
+ for (int i = 0; i < 16; i += 4)
+ decode_block3_ani(avctx, gb1, gb2, x+i, y+j, dst, linesize, ref, ref_linesize, mv_x, mv_y, ori_delta);
+ }
+ } else {
+ for (int j = 0; j < 16; j += 4)
+ for (int i = 0; i < 16; i += 4)
+ decode_block2_ani(avctx, gb1, gb2, x+i, y+j, dst, linesize, ori_delta);
+ }
+ return 0;
+}
+
+static int decode_mbedge_ani(AVCodecContext *avctx, GetBitContext * gb1, GetByteContext * gb2, int xpos, int ypos,
+ uint8_t * dst, int linesize, const uint8_t * ref, int ref_linesize, const uint16_t * ori_delta)
+{
+ if (get_bits1(gb1)) {
+ avpriv_request_sample(avctx, "skip edge");
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (int y = ypos; y < FFMIN(ypos + 16, avctx->height); y += 4) {
+ for (int x = xpos; x < FFMIN(xpos + 16, avctx->width); x += 4) {
+ if (x + 4 <= avctx->width && y + 4 <= avctx->height) {
+ int mode = get_bits(gb1, 2);
+ if (mode < 3) {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ if (x + i < avctx->width && y + j < avctx->height) {
+ decode_pixel(avctx, gb1, gb2, ori_delta,
+ dst + (y+j)*linesize + (x+i)*2,
+ dst, linesize, x+i+qmage_dir[mode].x, y+j+qmage_dir[mode].y);
+ }
+ } else {
+ if (x > 0)
+ copy_edge(dst + y*linesize + x*2, linesize, FFMIN(avctx->width - x, 4), FFMIN(avctx->height - y, 4));
+ }
+ } else {
+ for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 4; i++)
+ if (x + i < avctx->width && y + j < avctx->height)
+ AV_WN16A(dst + (y+j)*linesize + (x+i)*2, bytestream2_get_le16(gb2));
+ }
+ }
+ }
+ return 0;
+}
+
+static int decode_a9ll_ani(AVCodecContext *avctx, const uint8_t * data, int size, uint8_t * dst, int dst_linesize, const uint8_t * ref, int ref_linesize)
+{
+ Context * s = avctx->priv_data;
+ GetBitContext gb1;
+ GetByteContext gb2;
+ int gb1_start;
+ const uint16_t * ori_delta;
+ int ret;
+
+ if (size < s->header_size + 8)
+ return AVERROR_INVALIDDATA;
+
+ gb1_start = AV_RL32(data + s->header_size);
+ if (gb1_start < s->header_size + 8 || gb1_start > size)
+ return AVERROR_INVALIDDATA;
+ if ((ret = init_get_bits8(&gb1, data + s->header_size + 8, size - s->header_size - 8)) < 0)
+ return ret;
+ bytestream2_init(&gb2, data + gb1_start, size - gb1_start);
+
+ ori_delta = qmage_ori_delta[s->qversion != QVERSION_1_43_LESS];
+
+ for (int y = 0; y < avctx->height; y += 16) {
+ for (int x = 0; x < avctx->width; x += 16) {
+ if (avctx->width - x >= 16 && avctx->height - y >= 16) {
+ if (decode_mb_ani(avctx, &gb1, &gb2, x, y, dst, dst_linesize, ref, ref_linesize, ori_delta) < 0)
+ return AVERROR_INVALIDDATA;
+ } else {
+ if (decode_mbedge_ani(avctx, &gb1, &gb2, x, y, dst, dst_linesize, ref, ref_linesize, ori_delta) < 0)
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ }
+ return 0;
+}
+
+static void memset32(uint8_t * dst, uint32_t v, int count)
+{
+ for (int i = 0; i < count; i++)
+ AV_WN32A(dst + i * 4, v);
+}
+
+static int read_value(GetByteContext * gb)
+{
+ int v = 0;
+ while (bytestream2_peek_byte(gb) == 0xff) {
+ bytestream2_skip(gb, 1);
+ v += 0xff;
+ }
+ return v + bytestream2_get_byte(gb);
+}
+
+static int decode_w2_aligned(AVCodecContext * avctx, GetByteContext * gb1, GetByteContext * gb2, GetByteContext * gb3, const uint8_t * data, int size, uint8_t * dst)
+{
+ int counter = 0, idx, run;
+ uint32_t val;
+ int dim = avctx->width * avctx->height * 2;
+ do {
+ idx = read_value(gb1);
+ if (!idx) {
+ val = bytestream2_get_le32(gb3);
+ AV_WN32A(dst + counter, val);
+ counter += 4;
+ } else {
+ idx--;
+ if (idx * 4 + 4 > size - 16)
+ return AVERROR_INVALIDDATA;
+ val = AV_RL32(data + 16 + idx * 4);
+ run = read_value(gb2) + 1;
+
+ memset32(dst + counter, val, FFMIN(run, (dim - counter) / 4));
+ counter += 4 * run;
+ }
+ } while (counter < dim);
+ return 0;
+}
+
+static int decode_w2_unaligned(AVCodecContext * avctx, GetByteContext * gb1, GetByteContext * gb2, GetByteContext * gb3, const uint8_t * data, int size, uint8_t * dst, int dst_linesize)
+{
+ int x = 0, y = 0, idx, run;
+ uint16_t v1, v2;
+
+ while (1) {
+ idx = read_value(gb1);
+ if (!idx) {
+#define WRITE_PIXEL(v) \
+ AV_WN16A(dst + y*dst_linesize + x*2, v); \
+ x++; \
+ if (x >= avctx->width) { \
+ x = 0; \
+ y++; \
+ if (y >= avctx->height) \
+ return 0; \
+ }
+ v1 = bytestream2_get_le16(gb3);
+ v2 = bytestream2_get_le16(gb3);
+ WRITE_PIXEL(v1)
+ WRITE_PIXEL(v2)
+ } else {
+ idx--;
+ if (idx * 4 + 4 > size - 16)
+ return AVERROR_INVALIDDATA;
+ v1 = AV_RL16(data + 16 + idx * 4);
+ v2 = AV_RL16(data + 16 + idx * 4 + 2);
+ run = read_value(gb2) + 1;
+ for (int i = 0; i < run; i++) {
+ WRITE_PIXEL(v1)
+ WRITE_PIXEL(v2)
+ }
+ }
+ }
+#undef WRITE_PIXEL
+ return 0;
+}
+
+static int decode_w2_pass_depth1(AVCodecContext *avctx, const uint8_t * data, int size, uint8_t * dst, int dst_linesize)
+{
+ int cnt_table, size_idx, size_run;
+ int start1, start2, start3;
+ GetByteContext gb1, gb2, gb3;
+
+ if (size < 16)
+ return AVERROR_INVALIDDATA;
+
+ cnt_table = AV_RL32(data);
+ size_idx = AV_RL32(data + 4);
+ size_run = AV_RL32(data + 8);
+
+ start1 = 16 + cnt_table*4;
+ start2 = start1 + size_idx;
+ start3 = start2 + size_run;
+
+ if (start1 >= size || start2 >= size || start3 > size)
+ return AVERROR_INVALIDDATA;
+
+ bytestream2_init(&gb1, data + start1, size - start1);
+ bytestream2_init(&gb2, data + start2, size - start2);
+ bytestream2_init(&gb3, data + start3, size - start3);
+
+ if (dst_linesize == avctx->width * 2)
+ return decode_w2_aligned(avctx, &gb1, &gb2, &gb3, data, size, dst);
+
+ return decode_w2_unaligned(avctx, &gb1, &gb2, &gb3, data, size, dst, dst_linesize);
+}
+
+static int strip1(GetBitContext * gb1, GetByteContext * gb2, GetByteContext * gb3, int * rel, uint8_t * dst, int d_pos)
+{
+ AV_WN32A(dst + d_pos, bytestream2_get_le32(gb3));
+ d_pos += 4;
+ for (int i = 0; i < 6; i++) {
+ uint16_t v;
+ if (!(i & 1)) {
+ if (!get_bits1(gb1))
+ *rel = get_bits1(gb1) ? bytestream2_get_byte(gb2)
+ : bytestream2_get_le16(gb3);
+ }
+ if (!get_bits1(gb1)) {
+ if (!get_bits1(gb1)) {
+ int pos = d_pos - *rel*2;
+ if (pos < 0)
+ return -1;
+ v = AV_RN16A(dst + pos) ^ qmage_diff[bytestream2_get_byte(gb2)];
+ } else {
+ v = bytestream2_get_ne16(gb3);
+ }
+ } else {
+ int pos = d_pos - *rel*2;
+ if (pos < 0)
+ return -1;
+ v = AV_RN16A(dst + d_pos - *rel*2);
+ }
+ AV_WN16A(dst + d_pos, v);
+ d_pos += 2;
+ }
+ return 0;
+}
+
+static int strip2(GetBitContext * gb1, GetByteContext * gb2, GetByteContext * gb3, int * rel, uint8_t * dst, int d_pos)
+{
+ int mask = bytestream2_get_byte(gb2);
+ for (int i = 0; i < 8; i++) {
+ uint16_t v;
+ if (!(i & 1)) {
+ if (!get_bits1(gb1)) {
+ *rel = get_bits1(gb1) ? bytestream2_get_byte(gb2)
+ : bytestream2_get_le16(gb3);
+ }
+ }
+ if (!(mask & (1 << (7 - i)))) {
+ if (!get_bits1(gb1)) {
+ int pos = d_pos - *rel*2;
+ if (pos < 0)
+ return -1;
+ v = AV_RN16A(dst + pos) ^ qmage_diff[bytestream2_get_byte(gb2)];
+ } else {
+ v = bytestream2_get_ne16(gb3);
+ }
+ } else {
+ int pos = d_pos - *rel*2;
+ if (pos < 0)
+ return -1;
+ v = AV_RN16A(dst + d_pos - *rel* 2);
+ }
+ AV_WN16A(dst + d_pos, v);
+ d_pos += 2;
+ }
+ return 0;
+}
+
+static int decode_w2_pass_depth2(AVCodecContext *avctx, const uint8_t * data, int size, uint8_t * dst, int dst_linesize)
+{
+ int bsize, ret;
+ uint8_t * bdata;
+ int len1, len2, rel = 1, d_pos;
+ GetBitContext gb1;
+ GetByteContext gb2, gb3;
+
+ if (size < 12)
+ return AVERROR_INVALIDDATA;
+
+ bsize = AV_RL32(data);
+ if (bsize < 16)
+ return AVERROR_INVALIDDATA;
+
+ bdata = av_malloc(bsize);
+ if (!bdata)
+ return AVERROR(ENOMEM);
+
+ len1 = AV_RL32(data + 4);
+ len2 = AV_RL32(data + 8);
+ if ((ret = init_get_bits8(&gb1, data + 12, size - 12)) < 0) {
+ free(bdata);
+ return ret;
+ }
+ bytestream2_init(&gb2, data + 12 + len1, size - 12 - len1);
+ bytestream2_init(&gb3, data + 12 + len1 + len2, size - 12 - len1 - len2);
+
+ if (strip1(&gb1, &gb2, &gb3, &rel, bdata, 0) < 0) {
+ free(bdata);
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (d_pos = 16; d_pos < (bsize & ~15); d_pos += 16) {
+ if (!get_bits1(&gb1)) {
+ if (!get_bits1(&gb1)) {
+ bytestream2_get_buffer(&gb3, bdata + d_pos, 16);
+ } else {
+ if (d_pos - rel*2 < 0) {
+ free(bdata);
+ return AVERROR_INVALIDDATA;
+ }
+ for (int j = 0; j < 8; j++)
+ AV_WN16A(bdata + d_pos + j*2, AV_RN16A(bdata + d_pos - rel*2 + j*2));
+ }
+ } else {
+ if (strip2(&gb1, &gb2, &gb3, &rel, bdata, d_pos) < 0) {
+ free(bdata);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ }
+
+ if (bsize & 15)
+ bytestream2_get_buffer(&gb2, bdata + d_pos, bsize & 15);
+
+ ret = decode_w2_pass_depth1(avctx, bdata, bsize, dst, dst_linesize);
+ av_free(bdata);
+ return ret;
+}
+
+static av_cold int qmage_decode_init(AVCodecContext *avctx)
+{
+ Context * s = avctx->priv_data;
+
+ avctx->pix_fmt = AV_PIX_FMT_RGB565;
+
+ s->last_frame = av_frame_alloc();
+ if (!s->last_frame)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
+static av_cold int qmage_decode_close(AVCodecContext *avctx)
+{
+ Context *s = avctx->priv_data;
+ av_frame_free(&s->last_frame);
+ return 0;
+}
+
+static int qmage_decode_frame(AVCodecContext *avctx, AVFrame *frame,
+ int *got_frame, AVPacket *avpkt)
+{
+ Context * s = avctx->priv_data;
+ int ret;
+
+ ret = decode_header(avctx, avpkt);
+ if (ret < 0)
+ return ret;
+
+ ret = ff_get_buffer(avctx, frame, 0);
+ if (ret < 0)
+ return ret;
+
+ if (s->mode) {
+ if (s->current_frame_number == 1) {
+ frame->flags |= AV_FRAME_FLAG_KEY;
+ decode_a9ll(avctx, avpkt->data, avpkt->size, frame->data[0], frame->linesize[0]);
+ } else {
+ decode_a9ll_ani(avctx, avpkt->data, avpkt->size, frame->data[0], frame->linesize[0],
+ s->last_frame->data[0], s->last_frame->linesize[0]);
+ }
+ } else {
+ frame->flags |= AV_FRAME_FLAG_KEY;
+ switch(s->encoder_mode) {
+ case QCODEC_W2_PASS: break;
+ default:
+ avpriv_request_sample(avctx, "encoder_mode=%d", s->encoder_mode);
+ return AVERROR_INVALIDDATA;
+ }
+ switch (s->depth) {
+ case 1:
+ decode_w2_pass_depth1(avctx, avpkt->data + s->header_size, avpkt->size - s->header_size, frame->data[0], frame->linesize[0]);
+ break;
+ case 2:
+ decode_w2_pass_depth2(avctx, avpkt->data + s->header_size, avpkt->size - s->header_size, frame->data[0], frame->linesize[0]);
+ break;
+ default:
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ if ((ret = av_frame_replace(s->last_frame, frame)) < 0)
+ return ret;
+
+ *got_frame = 1;
+
+ return avpkt->size;
+}
+
+const FFCodec ff_qmage_decoder = {
+ .p.name = "qmage",
+ CODEC_LONG_NAME("Quram Qmage"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_QMAGE,
+ .p.capabilities = AV_CODEC_CAP_DR1,
+ .priv_data_size = sizeof(Context),
+ .init = qmage_decode_init,
+ .close = qmage_decode_close,
+ FF_CODEC_DECODE_CB(qmage_decode_frame),
+};
--
2.45.2
-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20241121/31e057b0/attachment.sig>
More information about the ffmpeg-devel
mailing list