[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