[FFmpeg-devel] [PATCH v6 1/4] lavc: add FLIF decoding support

Anamitra Ghorui aghorui at teknik.io
Sat Aug 22 21:39:32 EEST 2020


v2: Fix faulty patch
v3: Fix addressed errors, Add interlaced decoding support
v4: Fix Further cosmetics, C.Bucket Transform reading errors, Atomise patch
v5: Fix faulty patch
v6: Address pointed out errors, use av_freep everywhere, further cosmetics,
    redundancies.

Test files are available here: https://0x0.st/iYs_.zip

Co-authored-by: Anamitra Ghorui <aghorui at teknik.io>
Co-authored-by: Kartik K Khullar <kartikkhullar840 at gmail.com>

Signed-off-by: Anamitra Ghorui <aghorui at teknik.io>
---
 libavcodec/Makefile            |    3 +
 libavcodec/allcodecs.c         |    1 +
 libavcodec/codec_desc.c        |    7 +
 libavcodec/codec_id.h          |    1 +
 libavcodec/flif16.c            |  204 +++
 libavcodec/flif16.h            |  287 ++++
 libavcodec/flif16_parser.c     |  193 +++
 libavcodec/flif16_rangecoder.c |  800 +++++++++
 libavcodec/flif16_rangecoder.h |  400 +++++
 libavcodec/flif16_transform.c  | 2895 ++++++++++++++++++++++++++++++++
 libavcodec/flif16_transform.h  |  124 ++
 libavcodec/flif16dec.c         | 1764 +++++++++++++++++++
 libavcodec/parsers.c           |    1 +
 libavcodec/version.h           |    2 +-
 14 files changed, 6681 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/flif16.c
 create mode 100644 libavcodec/flif16.h
 create mode 100644 libavcodec/flif16_parser.c
 create mode 100644 libavcodec/flif16_rangecoder.c
 create mode 100644 libavcodec/flif16_rangecoder.h
 create mode 100644 libavcodec/flif16_transform.c
 create mode 100644 libavcodec/flif16_transform.h
 create mode 100644 libavcodec/flif16dec.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 99019cbde8..5f3482f587 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -330,6 +330,8 @@ OBJS-$(CONFIG_FLASHSV_ENCODER)         += flashsvenc.o
 OBJS-$(CONFIG_FLASHSV2_ENCODER)        += flashsv2enc.o
 OBJS-$(CONFIG_FLASHSV2_DECODER)        += flashsv.o
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
+OBJS-$(CONFIG_FLIF16_DECODER)          += flif16dec.o flif16_rangecoder.o \
+                                          flif16.o flif16_transform.o
 OBJS-$(CONFIG_FMVC_DECODER)            += fmvc.o
 OBJS-$(CONFIG_FOURXM_DECODER)          += 4xm.o
 OBJS-$(CONFIG_FRAPS_DECODER)           += fraps.o
@@ -1073,6 +1075,7 @@ OBJS-$(CONFIG_DVD_NAV_PARSER)          += dvd_nav_parser.o
 OBJS-$(CONFIG_DVDSUB_PARSER)           += dvdsub_parser.o
 OBJS-$(CONFIG_FLAC_PARSER)             += flac_parser.o flacdata.o flac.o \
                                           vorbis_data.o
+OBJS-$(CONFIG_FLIF16_PARSER)           += flif16_parser.o
 OBJS-$(CONFIG_G723_1_PARSER)           += g723_1_parser.o
 OBJS-$(CONFIG_G729_PARSER)             += g729_parser.o
 OBJS-$(CONFIG_GIF_PARSER)              += gif_parser.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 729d2fd9ad..4ffb791df4 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -120,6 +120,7 @@ extern AVCodec ff_flashsv_decoder;
 extern AVCodec ff_flashsv2_encoder;
 extern AVCodec ff_flashsv2_decoder;
 extern AVCodec ff_flic_decoder;
+extern AVCodec ff_flif16_decoder;
 extern AVCodec ff_flv_encoder;
 extern AVCodec ff_flv_decoder;
 extern AVCodec ff_fmvc_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 0ae6aee63b..11acb91b76 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1784,6 +1784,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("PFM (Portable FloatMap) image"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
     },
+    {
+        .id        = AV_CODEC_ID_FLIF16,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "flif16",
+        .long_name = NULL_IF_CONFIG_SMALL("FLIF16 (Free Lossless Image Format)"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 896ecb0ce0..5c4f2dd7d0 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -296,6 +296,7 @@ enum AVCodecID {
     AV_CODEC_ID_MV30,
     AV_CODEC_ID_NOTCHLC,
     AV_CODEC_ID_PFM,
+    AV_CODEC_ID_FLIF16,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/flif16.c b/libavcodec/flif16.c
new file mode 100644
index 0000000000..7c6aaf5d0f
--- /dev/null
+++ b/libavcodec/flif16.c
@@ -0,0 +1,204 @@
+/*
+ * FLIF16 Image Format Definitions
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * FLIF16 format definitions and functions.
+ */
+
+#include "flif16.h"
+#include "flif16_transform.h"
+
+/**
+ * Initialise property ranges for non interlaced images.
+ * @param[out] prop_ranges resultant ranges
+ * @param[in]  color ranges of each channel
+ * @param[in]  channels number of channels
+ */
+void ff_flif16_maniac_ni_prop_ranges_init(FLIF16MinMax *prop_ranges,
+                                          unsigned int *prop_ranges_size,
+                                          FLIF16RangesContext *ranges,
+                                          uint8_t plane,
+                                          uint8_t channels)
+{
+    int min = ff_flif16_ranges_min(ranges, plane);
+    int max = ff_flif16_ranges_max(ranges, plane);
+    int mind = min - max, maxd = max - min;
+    unsigned int top = 0;
+    unsigned int size = (((plane < 3) ? plane : 0) + 2 + 5) + ((plane < 3) && (ranges->num_planes > 3));
+    *prop_ranges_size = size;
+    if (plane < 3) {
+        for (int i = 0; i < plane; i++) {
+            prop_ranges[top].min   = ff_flif16_ranges_min(ranges, i);
+            prop_ranges[top++].max = ff_flif16_ranges_max(ranges, i); // pixels on previous planes
+        }
+        if (ranges->num_planes > 3)  {
+            prop_ranges[top].min   = ff_flif16_ranges_min(ranges, 3);
+            prop_ranges[top++].max = ff_flif16_ranges_max(ranges, 3); // pixel on alpha plane
+        }
+    }
+    prop_ranges[top].min = min;
+    prop_ranges[top++].max = max; // guess (median of 3)
+    prop_ranges[top].min = 0;
+    prop_ranges[top++].max = 2; // which predictor was it
+    for (int i = 0; i < 5; i++) {
+        prop_ranges[top].min = mind;
+        prop_ranges[top++].max = maxd;
+    }
+}
+
+void ff_flif16_maniac_prop_ranges_init(FLIF16MinMax *prop_ranges,
+                                       unsigned int *prop_ranges_size,
+                                       FLIF16RangesContext *ranges,
+                                       uint8_t plane,
+                                       uint8_t channels)
+{
+    int min = ff_flif16_ranges_min(ranges, plane);
+    int max = ff_flif16_ranges_max(ranges, plane);
+    unsigned int top = 0, pp;
+    int mind = min - max, maxd = max - min;
+    unsigned int size =   (((plane < 3) ? ((ranges->num_planes > 3) ? plane + 1 : plane) : 0) \
+                        + ((plane == 1 || plane == 2) ? 1 : 0) \
+                        + ((plane != 2) ? 2 : 0) + 1 + 5);
+    *prop_ranges_size = size;
+
+    if (plane < 3) {
+        for (pp = 0; pp < plane; pp++) {
+            prop_ranges[top].min = ff_flif16_ranges_min(ranges, pp);
+            prop_ranges[top++].max = ff_flif16_ranges_max(ranges, pp);
+        }
+        if (ranges->num_planes > 3) {
+            prop_ranges[top].min = ff_flif16_ranges_min(ranges, 3);
+            prop_ranges[top++].max = ff_flif16_ranges_max(ranges, 3);
+        }
+    }
+
+    prop_ranges[top].min = 0;
+    prop_ranges[top++].max = 2;
+
+    if (plane == 1 || plane == 2){
+        prop_ranges[top].min = ff_flif16_ranges_min(ranges, 0) - ff_flif16_ranges_max(ranges, 0);
+        prop_ranges[top++].max = ff_flif16_ranges_max(ranges, 0) - ff_flif16_ranges_min(ranges, 0); // luma prediction miss
+    }
+
+    for (int i = 0; i < 4; i++) {
+        prop_ranges[top].min = mind;
+        prop_ranges[top++].max = maxd;
+    }
+
+    prop_ranges[top].min = min;
+    prop_ranges[top++].max = max;
+
+    if (plane != 2) {
+      prop_ranges[top].min = mind;
+      prop_ranges[top++].max = maxd;
+      prop_ranges[top].min = mind;
+      prop_ranges[top++].max = maxd;
+    }
+}
+
+
+int ff_flif16_planes_init(FLIF16Context *s, FLIF16PixelData *frame,
+                          FLIF16ColorVal *const_plane_value)
+{
+    if (frame->seen_before >= 0)
+        return 0;
+
+    /* Multiplication overflow is dealt with in the decoder/encoder. */
+    for (int i = 0; i < s->num_planes; i++) {
+        switch (s->plane_mode[i]) {
+        case FLIF16_PLANEMODE_NORMAL:
+            frame->data[i] = av_malloc_array(s->width * s->height, sizeof(FLIF16ColorVal));
+            if (!frame->data[i])
+                return AVERROR(ENOMEM);
+            break;
+
+        case FLIF16_PLANEMODE_CONSTANT:
+            frame->data[i] = av_malloc(sizeof(FLIF16ColorVal));
+            if (!frame->data[i])
+                return AVERROR(ENOMEM);
+            ((FLIF16ColorVal *) frame->data[i])[0] = const_plane_value[i];
+            break;
+
+        case FLIF16_PLANEMODE_FILL:
+            frame->data[i] = av_malloc_array(s->width * s->height, sizeof(FLIF16ColorVal));
+            if (!frame->data[i])
+                return AVERROR(ENOMEM);
+            for (int k = 0; k < s->width * s->height; k++)
+                    ((FLIF16ColorVal *) frame->data[i])[k] = const_plane_value[i];
+            break;
+
+        default:
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    return 0;
+}
+
+
+static void ff_flif16_planes_free(FLIF16PixelData *frame, uint8_t num_planes,
+                                uint8_t lookback)
+{
+    for(uint8_t i = 0; i < (lookback ? MAX_PLANES : num_planes); i++) {
+        av_free(frame->data[i]);
+    }
+}
+
+FLIF16PixelData *ff_flif16_frames_init(uint16_t num_frames)
+{
+    FLIF16PixelData *frames = av_mallocz_array(num_frames, sizeof(*frames));
+    if (!frames)
+        return NULL;
+    for (int i = 0; i < num_frames; i++)
+        frames[i].seen_before = -1;
+    return frames;
+}
+
+FLIF16PixelData *ff_flif16_frames_resize(FLIF16PixelData *frames,
+                                         uint16_t curr_num_frames,
+                                         uint16_t new_num_frames)
+{
+    FLIF16PixelData *new_frames = av_realloc_f(frames, new_num_frames,
+                                               sizeof(*frames));
+    if (!new_frames)
+        return NULL;
+
+    for (int i = curr_num_frames; i < new_num_frames; i++)
+        new_frames[i].seen_before = -1;
+    return new_frames;
+}
+
+void ff_flif16_frames_free(FLIF16PixelData **frames, uint32_t num_frames,
+                           uint32_t num_planes, uint8_t lookback)
+{
+    for (int i = 0; i < num_frames; i++) {
+        if ((*frames)[i].seen_before >= 0)
+            continue;
+        ff_flif16_planes_free(&(*frames)[i], num_planes, lookback);
+        if ((*frames)[i].col_begin)
+            av_freep(&(*frames)[i].col_begin);
+        if ((*frames)[i].col_end)
+            av_freep(&(*frames)[i].col_end);
+    }
+
+    av_freep(frames);
+}
diff --git a/libavcodec/flif16.h b/libavcodec/flif16.h
new file mode 100644
index 0000000000..07686096c9
--- /dev/null
+++ b/libavcodec/flif16.h
@@ -0,0 +1,287 @@
+/*
+ * FLIF16 Image Format Definitions
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * FLIF16 format definitions and functions.
+ */
+
+#ifndef AVCODEC_FLIF16_H
+#define AVCODEC_FLIF16_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "avcodec.h"
+#include "libavutil/pixfmt.h"
+#include "flif16_rangecoder.h"
+
+#define MAX_PLANES 5
+#define MAX_TRANSFORMS 13
+#define MAX_PROPERTIES 12
+#define MAX_PREDICTORS 2
+#define MAX_PROP_RANGES 12
+
+#define VARINT_APPEND(a,x) (a) = (((a) << 7) | (uint32_t) ((x) & 127))
+#define ZOOM_ROWPIXELSIZE(zoomlevel) (1 << (((zoomlevel) + 1) / 2))
+#define ZOOM_COLPIXELSIZE(zoomlevel) (1 << (((zoomlevel)) / 2))
+#define ZOOM_HEIGHT(h, z) ((!h) ? 0 : (1 + ((h) - 1) / ZOOM_ROWPIXELSIZE(z)))
+#define ZOOM_WIDTH(w, z) ((!w) ? 0 : (1 + ((w) - 1) / ZOOM_COLPIXELSIZE(z)))
+#define MEDIAN3(a, b, c) (((a) < (b)) ? (((b) < (c)) ?  (b) : ((a) < (c) ? (c) : (a))) : (((a) < (c)) ? (a) : ((b) < (c) ? (c) : (b))))
+
+static const uint8_t flif16_header[4] = "FLIF";
+
+// Pixeldata types
+static const enum AVPixelFormat flif16_out_frame_type[][2] = {
+    { -1,  -1 }, // Padding
+    { AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16 },
+    { -1 , -1 }, // Padding
+    { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48  },
+    { AV_PIX_FMT_RGB32, AV_PIX_FMT_RGBA64 }
+};
+
+typedef enum FLIF16Plane {
+    FLIF16_PLANE_Y = 0,
+    FLIF16_PLANE_CO,
+    FLIF16_PLANE_CG,
+    FLIF16_PLANE_ALPHA,
+    FLIF16_PLANE_LOOKBACK // Frame lookback
+} FLIF16Plane;
+
+typedef enum FLIF16PlaneMode {
+    FLIF16_PLANEMODE_CONSTANT = 0, ///< A true constant plane
+    FLIF16_PLANEMODE_NORMAL,       ///< A normal pixel matrix
+    FLIF16_PLANEMODE_FILL          /**< A constant plane that is later manipulated
+                                        by transforms, making it nonconstant and
+                                        allocating a plane for it */
+} FLIF16PlaneMode;
+
+typedef struct FLIF16PixelData {
+    int8_t seen_before;  // Required by FrameDup
+    uint32_t *col_begin; // Required by FrameShape
+    uint32_t *col_end;   // Required by FrameShape
+    int s_r[MAX_PLANES];
+    int s_c[MAX_PLANES];
+    void *data[MAX_PLANES];
+    uint8_t palette;
+} FLIF16PixelData;
+
+typedef int32_t FLIF16ColorVal;
+
+typedef struct FLIF16Context {
+    FLIF16MANIACContext maniac_ctx;
+    FLIF16RangeCoder rc;
+
+    // Dimensions
+    uint32_t width;
+    uint32_t height;
+    uint32_t num_frames;
+    uint32_t meta;       ///< Size of a meta chunk
+
+    // Primary Header
+    uint32_t bpc;         ///< 2 ^ Bits per channel - 1
+    uint16_t *framedelay; ///< Frame delay for each frame
+    uint8_t  ia;          ///< Is image interlaced or/and animated or not
+    uint8_t  num_planes;  ///< Number of planes
+    uint8_t  loops;       ///< Number of times animation loops
+    FLIF16PlaneMode  plane_mode[MAX_PLANES];
+
+    // Transform flags
+    uint8_t framedup;
+    uint8_t frameshape;
+    uint8_t framelookback;
+} FLIF16Context;
+
+typedef struct FLIF16RangesContext {
+    uint8_t r_no;
+    uint8_t num_planes;
+    void *priv_data;
+} FLIF16RangesContext;
+
+typedef struct FLIF16Ranges {
+    uint8_t priv_data_size;
+    FLIF16ColorVal (*min)(FLIF16RangesContext *ranges, int plane);
+    FLIF16ColorVal (*max)(FLIF16RangesContext *ranges, int plane);
+    void (*minmax)(FLIF16RangesContext *ranges, int plane,
+                   FLIF16ColorVal *prev_planes, FLIF16ColorVal *minv,
+                   FLIF16ColorVal *maxv);
+    void (*snap)(FLIF16RangesContext *r_ctx, int plane,
+                 FLIF16ColorVal *prev_planes, FLIF16ColorVal *minv,
+                 FLIF16ColorVal *maxv, FLIF16ColorVal *v);
+    uint8_t is_static;
+    void (*close)(FLIF16RangesContext *ctx);
+} FLIF16Ranges;
+
+typedef struct FLIF16TransformContext {
+    uint8_t t_no;
+    unsigned int segment; ///< Segment the code is executing in.
+    int i;                ///< Variable to store iteration number.
+    void *priv_data;
+} FLIF16TransformContext;
+
+typedef struct FLIF16Transform {
+    int16_t priv_data_size;
+    //Functions
+    int (*init) (FLIF16TransformContext *t_ctx, FLIF16RangesContext *r_ctx);
+    int (*read) (FLIF16TransformContext *t_ctx, FLIF16Context *ctx,
+                 FLIF16RangesContext *r_ctx);
+    FLIF16RangesContext *(*meta) (FLIF16Context *ctx, FLIF16PixelData *frame,
+                                  uint32_t frame_count, FLIF16TransformContext *t_ctx,
+                                  FLIF16RangesContext *r_ctx);
+    int (*process) (FLIF16Context *ctx, FLIF16TransformContext *t_ctx,
+                    FLIF16RangesContext *src_ctx, FLIF16PixelData *frame);
+    void (*forward) (FLIF16Context *ctx, FLIF16TransformContext *t_ctx, FLIF16PixelData *frame);
+    int (*write) (FLIF16Context *enc_ctx, FLIF16TransformContext *t_ctx,
+                  FLIF16RangesContext *src_ctx);
+    void (*reverse) (FLIF16Context *ctx, FLIF16TransformContext *t_ctx, FLIF16PixelData *frame,
+                     uint32_t stride_row, uint32_t stride_col);
+    void (*configure) (FLIF16TransformContext *ctx, const int setting);
+    void (*close) (FLIF16TransformContext *t_ctx);
+} FLIF16Transform;
+
+void ff_flif16_maniac_ni_prop_ranges_init(FLIF16MinMax *prop_ranges,
+                                          unsigned int *prop_ranges_size,
+                                          FLIF16RangesContext *ranges,
+                                          uint8_t plane,
+                                          uint8_t channels);
+
+void ff_flif16_maniac_prop_ranges_init(FLIF16MinMax *prop_ranges,
+                                       unsigned int *prop_ranges_size,
+                                       FLIF16RangesContext *ranges,
+                                       uint8_t plane,
+                                       uint8_t channels);
+
+int ff_flif16_planes_init(FLIF16Context *s, FLIF16PixelData *frame,
+                          int32_t *const_plane_value);
+
+FLIF16PixelData *ff_flif16_frames_init(uint16_t num_frames);
+
+FLIF16PixelData *ff_flif16_frames_resize(FLIF16PixelData *frames,
+                                         uint16_t curr_num_frames,
+                                         uint16_t new_num_frames);
+
+void ff_flif16_frames_free(FLIF16PixelData **frames, uint32_t num_frames,
+                           uint32_t num_planes, uint8_t lookback);
+
+
+
+/*
+ * All constant plane pixel setting should be illegal in theory.
+ */
+
+static inline void ff_flif16_pixel_set(FLIF16Context *s, FLIF16PixelData *frame,
+                                       uint8_t plane, uint32_t row, uint32_t col,
+                                       FLIF16ColorVal value)
+{
+    ((FLIF16ColorVal *) frame->data[plane])[s->width * row + col] = value;
+}
+
+static inline FLIF16ColorVal ff_flif16_pixel_get(FLIF16Context *s,
+                                                 FLIF16PixelData *frame,
+                                                 uint8_t plane, uint32_t row,
+                                                 uint32_t col)
+{
+    if (s->plane_mode[plane]) {
+        return ((FLIF16ColorVal *) frame->data[plane])[s->width * row + col];
+    } else
+        return ((FLIF16ColorVal *) frame->data[plane])[0];
+}
+
+
+static inline void ff_flif16_pixel_setz(FLIF16Context *s,
+                                        FLIF16PixelData *frame,
+                                        uint8_t plane, int z, uint32_t row,
+                                        uint32_t col, FLIF16ColorVal value)
+{
+    ((FLIF16ColorVal *) frame->data[plane])[(row * ZOOM_ROWPIXELSIZE(z)) * s->width +
+                                            (col * ZOOM_COLPIXELSIZE(z))] = value;
+}
+
+static inline FLIF16ColorVal ff_flif16_pixel_getz(FLIF16Context *s,
+                                                  FLIF16PixelData *frame,
+                                                  uint8_t plane, int z,
+                                                  size_t row, size_t col)
+{
+    if (s->plane_mode[plane]) {
+        return ((FLIF16ColorVal *) frame->data[plane])[(row * ZOOM_ROWPIXELSIZE(z)) *
+                                                        s->width + (col * ZOOM_COLPIXELSIZE(z))];
+    } else {
+        return ((FLIF16ColorVal *) frame->data[plane])[0];
+    }
+}
+
+static inline void ff_flif16_prepare_zoomlevel(FLIF16Context *s,
+                                               FLIF16PixelData *frame,
+                                               uint8_t plane, int z)
+{
+    frame->s_r[plane] = ZOOM_ROWPIXELSIZE(z) * s->width;
+    frame->s_c[plane] = ZOOM_COLPIXELSIZE(z);
+}
+
+static inline FLIF16ColorVal ff_flif16_pixel_get_fast(FLIF16Context *s,
+                                                      FLIF16PixelData *frame,
+                                                      uint8_t plane, uint32_t row,
+                                                      uint32_t col)
+{
+    if (s->plane_mode[plane]) {
+        return ((FLIF16ColorVal *) frame->data[plane])[row * frame->s_r[plane] + col * frame->s_c[plane]];
+    } else
+        return ((FLIF16ColorVal *) frame->data[plane])[0];
+    return 0;
+}
+
+static inline void ff_flif16_pixel_set_fast(FLIF16Context *s,
+                                            FLIF16PixelData *frame,
+                                            uint8_t plane, uint32_t row,
+                                            uint32_t col, FLIF16ColorVal value)
+{
+    ((FLIF16ColorVal *) frame->data[plane])[row * frame->s_r[plane] + col * frame->s_c[plane]] = value;
+}
+
+static inline void ff_flif16_copy_cols(FLIF16Context *s,
+                                       FLIF16PixelData *dest,
+                                       FLIF16PixelData *src, uint8_t plane,
+                                       uint32_t row, uint32_t col_start,
+                                       uint32_t col_end)
+{
+    FLIF16ColorVal *desti = ((FLIF16ColorVal *) dest->data[plane]) +
+                            s->width * row + col_start;
+    FLIF16ColorVal *destif = ((FLIF16ColorVal *) dest->data[plane]) +
+                             s->width * row + col_end;
+    FLIF16ColorVal *srci = ((FLIF16ColorVal *) src->data[plane]) +
+                            s->width * row + col_start;
+    while (desti < destif) {
+        *(desti++) = *(srci++);
+    }
+}
+
+static inline void ff_flif16_copy_cols_stride(FLIF16Context *s,
+                                              FLIF16PixelData *dest,
+                                              FLIF16PixelData *src, uint8_t plane,
+                                              uint32_t row, uint32_t col_start,
+                                              uint32_t col_end, uint32_t stride)
+{
+    for (uint32_t col = col_start; col < col_end; col += stride) {
+        ((FLIF16ColorVal *) dest->data[plane])[s->width * row + col] =
+        ((FLIF16ColorVal *) src->data[plane])[s->width * row + col];
+    }
+}
+#endif /* AVCODEC_FLIF16_H */
diff --git a/libavcodec/flif16_parser.c b/libavcodec/flif16_parser.c
new file mode 100644
index 0000000000..ca2569835e
--- /dev/null
+++ b/libavcodec/flif16_parser.c
@@ -0,0 +1,193 @@
+/*
+ * FLIF16 parser
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ /**
+  * @file
+  * FLIF16 parser
+  */
+
+#include "flif16.h"
+#include "parser.h"
+#include "libavutil/avassert.h"
+#include "libavutil/bswap.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef enum FLIF16ParseStates {
+    FLIF16_PARSE_INIT_STATE = 0,
+    FLIF16_PARSE_HEADER,
+    FLIF16_PARSE_METADATA,
+    FLIF16_PARSE_BITSTREAM
+} FLIF16ParseStates;
+
+typedef struct FLIF16ParseContext {
+    ParseContext pc;
+    FLIF16ParseStates state; ///< The section of the file the parser is in currently.
+    unsigned int index;      ///< An index based on the current state.
+    uint8_t animated;        ///< Is image animated or not
+    uint8_t varint;          ///< Number of varints to process in sequence
+    uint32_t width;
+    uint32_t height;
+    uint32_t frames;
+    uint32_t meta;           ///< Size of a meta chunk
+    uint32_t count;
+} FLIF16ParseContext;
+
+static int flif16_find_frame(FLIF16ParseContext *f, const uint8_t *buf,
+                             int buf_size)
+{
+    int next = END_NOT_FOUND;
+    int index;
+
+    for (index = 0; index < buf_size; index++) {
+        switch (f->state) {
+        case FLIF16_PARSE_INIT_STATE:
+            if (!memcmp(flif16_header, buf + index, 4))
+                f->state = FLIF16_PARSE_HEADER;
+            f->index++;
+            break;
+
+        case FLIF16_PARSE_HEADER:
+            if (f->index == 3 + 1) {
+                // See whether image is animated or not
+                f->animated = (((buf[index] >> 4) > 4) ? 1 : 0);
+            } else if (f->index == (3 + 1 + 1)) {
+                // Start - 1 of the first varint
+                f->varint = 1;
+            } else if (f->varint) {
+                // Count varint
+                if (f->count == 5)
+                        return AVERROR_INVALIDDATA;
+
+                switch (f->varint) {
+                case 1:
+                    VARINT_APPEND(f->width, buf[index]);
+                    break;
+
+                case 2:
+                    VARINT_APPEND(f->height, buf[index]);
+                    break;
+
+                case 3:
+                    VARINT_APPEND(f->frames, buf[index]);
+                    break;
+                }
+                if (buf[index] < 128) {
+                    if (f->varint < (2 + f->animated)) {
+                        switch (f->varint) {
+                        case 1: f->width++;  break;
+                        case 2: f->height++; break;
+                        }
+                        f->varint++;
+                        f->count = 0;
+                    } else {
+                        if (f->varint == 2)
+                            f->height++;
+                        if (f->animated)
+                            f->frames += 2;
+                        else
+                            f->frames = 1;
+                        f->state = FLIF16_PARSE_METADATA;
+                        f->varint = 0;
+                        f->index = 0;
+                        f->count = 0;
+                        continue;
+                    }
+                } else {
+                    f->count++;
+                }
+            }
+            f->index++;
+            break;
+
+        case FLIF16_PARSE_METADATA:
+            if (f->index == 0) {
+                // Identifier for the bitstream chunk is a null byte.
+                if (buf[index] == 0) {
+                    f->state = FLIF16_PARSE_BITSTREAM;
+                    return buf_size;
+                }
+            } else if (f->index < 3) {
+                // nop
+            } else if (f->index == 3) {
+                // Handle the size varint
+                f->varint = 1;
+            } else if (f->varint) {
+                if (f->count == 9)
+                    return AVERROR(ENOMEM);
+                if (buf[index] < 128) {
+                    f->varint = 0;
+                    f->count = 0;
+                }
+                VARINT_APPEND(f->meta, buf[index]);
+                f->count++;
+            } else if (f->meta > 1) {
+                // Increment varint until equal to size
+                f->meta--;
+            } else {
+                f->meta = 0;
+                f->index = 0;
+                continue;
+            }
+            f->index++;
+            break;
+
+        case FLIF16_PARSE_BITSTREAM:
+            /*
+             * Since we cannot find the end of the bitstream without any
+             * processing, we will simply return each read chunk as a packet
+             * to the decoder.
+             */
+            return buf_size;
+        }
+    }
+
+    return next;
+}
+
+static int flif16_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                        const uint8_t **poutbuf, int *poutbuf_size,
+                        const uint8_t *buf, int buf_size)
+{
+    FLIF16ParseContext *fpc = s->priv_data;
+    int next;
+
+    next = flif16_find_frame(fpc, buf, buf_size);
+
+    if (ff_combine_frame(&fpc->pc, next, &buf, &buf_size) < 0) {
+        *poutbuf      = NULL;
+        *poutbuf_size = 0;
+        return buf_size;
+    }
+
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+
+    return next;
+}
+
+AVCodecParser ff_flif16_parser = {
+    .codec_ids      = { AV_CODEC_ID_FLIF16 },
+    .priv_data_size = sizeof(FLIF16ParseContext),
+    .parser_parse   = flif16_parse,
+    .parser_close   = ff_parse_close,
+};
diff --git a/libavcodec/flif16_rangecoder.c b/libavcodec/flif16_rangecoder.c
new file mode 100644
index 0000000000..764da9cae9
--- /dev/null
+++ b/libavcodec/flif16_rangecoder.c
@@ -0,0 +1,800 @@
+/*
+ * Range coder for FLIF16
+ * Copyright (c) 2020, Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ /**
+  * @file
+  * Range coder for FLIF16
+  */
+
+#include "avcodec.h"
+#include "libavutil/common.h"
+#include "flif16_rangecoder.h"
+#include "flif16.h"
+
+/**
+ * Initializes the range decoder
+ * @param rc Pointer to the rangecoder struct
+ * @param gb Pointer to the encoded bytestream
+ * @returns AVERROR(EAGAIN) on insufficient buffer, 0 on success.
+ */
+int ff_flif16_rac_init(FLIF16RangeCoder *rc, GetByteContext *gb)
+{
+    int ret = 0;
+
+    if(bytestream2_get_bytes_left(gb) < FLIF16_RAC_MAX_RANGE_BYTES)
+        ret = AVERROR(EAGAIN);
+
+    // This is used to check whether the function is being run for the first
+    // time or not.
+    if (!rc->bytestream)
+        rc->range = FLIF16_RAC_MAX_RANGE;
+    rc->bytestream = gb;
+
+    for (; rc->range > 1 && bytestream2_get_bytes_left(rc->bytestream) > 0;
+         rc->range >>= 8) {
+        rc->low <<= 8;
+        rc->low |= bytestream2_get_byte(rc->bytestream);
+    }
+
+    if (rc->range <= 1)
+        rc->range = FLIF16_RAC_MAX_RANGE;
+
+    return ret;
+}
+
+static uint8_t ff_flif16_rac_get(FLIF16RangeCoder *rc, uint32_t chance,
+                                 uint8_t *target)
+{
+    if (rc->low >= rc->range - chance) {
+        rc->low -= rc->range - chance;
+        rc->range = chance;
+        *target = 1;
+    } else {
+        rc->range -= chance;
+        *target = 0;
+    }
+
+    return 1;
+}
+
+uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
+                               uint8_t *target)
+{
+    return ff_flif16_rac_get(rc, rc->range >> 1, target);
+}
+
+uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc,
+                                   uint64_t b12, uint8_t *target)
+{
+    uint32_t ret = ((rc->range) * b12 + 0x800) >> 12;
+    return ff_flif16_rac_get(rc, ret, target);
+}
+
+/**
+ * Reads a Uniform Symbol Coded Integer.
+ */
+int ff_flif16_rac_read_uni_int(FLIF16RangeCoder *rc,
+                               int min, int len,
+                               void *target, int type)
+{
+    int med;
+    uint8_t bit;
+
+    if (!rc->active) {
+        rc->min = min;
+        rc->len = len;
+        rc->active = 1;
+    }
+
+    if (rc->len > 0) {
+        ff_flif16_rac_read_bit(rc, &bit);
+        med = (rc->len) / 2;
+        if (bit) {
+            rc->min += med + 1;
+            rc->len -= med + 1;
+        } else {
+            rc->len = med;
+        }
+        return 0;
+    } else {
+        switch (type) {
+        case FLIF16_RAC_UNI_INT8:
+            *((int8_t *) target) = rc->min;
+            break;
+
+        case FLIF16_RAC_UNI_INT16:
+            *((int16_t *) target) = rc->min;
+            break;
+
+        case FLIF16_RAC_UNI_INT32:
+            *((int32_t *) target) = rc->min;
+            break;
+        }
+        rc->active = 0;
+        return 1;
+    }
+}
+
+// Nearzero integer definitions
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+static inline void ff_flif16_chance_estim(FLIF16RangeCoder *rc,
+                                          uint16_t chance, uint8_t bit,
+                                          uint64_t *total)
+{
+    *total += rc->log4k.table[bit ? chance : 4096 - chance];
+}
+#endif
+
+// NearZero Integer Coder
+
+static inline int ff_flif16_rac_nz_read_internal(FLIF16RangeCoder *rc,
+                                                 FLIF16ChanceContext *ctx,
+                                                 uint16_t type, uint8_t *target)
+{
+    if(!ff_flif16_rac_renorm(rc))
+        return 0; // EAGAIN condition
+    ff_flif16_rac_read_chance(rc, ctx->data[type], target);
+    ctx->data[type] = (!*target) ? rc->ct.zero_state[ctx->data[type]]
+                                 : rc->ct.one_state[ctx->data[type]];
+    return 1;
+}
+
+#define RAC_NZ_GET(rc, ctx, chance, target)                                    \
+    if (!ff_flif16_rac_nz_read_internal((rc), (ctx), (chance),                 \
+                                        (uint8_t *) (target))) {               \
+        goto need_more_data;                                                   \
+    }
+
+int ff_flif16_rac_read_nz_int(FLIF16RangeCoder *rc,
+                              FLIF16ChanceContext *ctx,
+                              int min, int max, int *target)
+{
+    uint8_t temp = 0;
+    if (min == max) {
+        *target = min;
+        rc->active = 0;
+        return 1;
+    }
+
+    if (!rc->active) {
+        rc->segment = 0;
+        rc->amin    = 1;
+        rc->active  = 1;
+        rc->sign    = 0;
+        rc->pos     = 0;
+    }
+
+    switch (rc->segment) {
+    case 0:
+        RAC_NZ_GET(rc, ctx, NZ_INT_ZERO, &temp);
+        if (temp) {
+            *target = 0;
+            rc->active = 0;
+            return 1;
+        }
+        rc->segment++;
+
+    case 1:
+        if (min < 0) {
+            if (max > 0) {
+                RAC_NZ_GET(rc, ctx, NZ_INT_SIGN, &rc->sign);
+            } else {
+                rc->sign = 0;
+            }
+        } else {
+            rc->sign = 1;
+        }
+        rc->amax = (rc->sign ? max : -min);
+        rc->emax = ff_log2(rc->amax);
+        rc->e    = ff_log2(rc->amin);
+        rc->segment++;
+
+    case 2:
+        for (; rc->e < rc->emax; rc->e++) {
+            RAC_NZ_GET(rc, ctx, NZ_INT_EXP(((rc->e << 1) + rc->sign)), &temp);
+            if (temp)
+                break;
+            temp = 0;
+        }
+        rc->have = (1 << rc->e);
+        rc->left = rc->have - 1;
+        rc->pos  = rc->e;
+        rc->segment++;
+
+        while (rc->pos > 0) {
+            rc->pos--;
+            rc->left >>= 1;
+            rc->minabs1 = rc->have | (1 << rc->pos);
+            rc->maxabs0 = rc->have | rc->left;
+
+            if (rc->minabs1 > rc->amax) {
+                continue;
+            } else if (rc->maxabs0 >= rc->amin) {
+    case 3:
+                RAC_NZ_GET(rc, ctx, NZ_INT_MANT(rc->pos), &temp);
+                if (temp)
+                    rc->have = rc->minabs1;
+                temp = 0;
+            } else {
+                rc->have = rc->minabs1;
+            }
+        }
+    }
+    *target = (rc->sign ? rc->have : -rc->have);
+    rc->active = 0;
+    return 1;
+
+    need_more_data:
+    return 0;
+}
+
+int ff_flif16_rac_read_gnz_int(FLIF16RangeCoder *rc,
+                               FLIF16ChanceContext *ctx,
+                               int min, int max, int *target)
+{
+    int ret;
+    if (min > 0) {
+        ret = ff_flif16_rac_read_nz_int(rc, ctx, 0, max - min, target);
+        if (ret)
+            *target += min;
+    } else if (max < 0) {
+        ret =  ff_flif16_rac_read_nz_int(rc, ctx, min - max, 0, target);
+        if (ret)
+            *target += max;
+    } else
+        ret = ff_flif16_rac_read_nz_int(rc, ctx, min, max, target);
+    return ret;
+
+}
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+// Multiscale chance definitions
+
+static inline void ff_flif16_multiscale_chance_set(FLIF16MultiscaleChance *c,
+                                                   uint16_t chance)
+{
+    for (int i = 0; i < MULTISCALE_CHANCETABLE_DEFAULT_SIZE; i++) {
+        c->chances[i] = chance;
+        c->quality[i] = 0;
+    }
+    c->best = 0;
+}
+
+static void ff_flif16_multiscale_chancetable_put(FLIF16RangeCoder *rc,
+                                                 FLIF16MultiscaleChanceContext *ctx,
+                                                 uint16_t type, uint8_t bit)
+{
+    FLIF16MultiscaleChance *c = &ctx->data[type];
+    uint64_t sbits, oqual;
+    for (int i = 0; i < MULTISCALE_CHANCETABLE_DEFAULT_SIZE; i++) {
+        sbits = 0;
+        ff_flif16_chance_estim(rc, c->chances[i], bit, &sbits);
+        oqual = c->quality[i];
+        c->quality[i] = (oqual * 255 + sbits * 4097 + 128) >> 8;
+        c->chances[i] = (bit) ? rc->mct->sub_table[i].one_state[c->chances[i]]
+                              : rc->mct->sub_table[i].zero_state[c->chances[i]];
+    }
+    for (int i = 0; i < MULTISCALE_CHANCETABLE_DEFAULT_SIZE; i++)
+        if (c->quality[i] < c->quality[c->best])
+            c->best = i;
+}
+
+static void ff_flif16_rac_read_multiscale_symbol(FLIF16RangeCoder *rc,
+                                                 FLIF16MultiscaleChanceContext *ctx,
+                                                 uint16_t type, uint8_t *target)
+{
+    ff_flif16_rac_read_chance(rc, ctx->data[type].chances[ctx->data[type].best], target);
+    ff_flif16_multiscale_chancetable_put(rc, ctx, type, *target);
+}
+
+static inline int ff_flif16_rac_nz_read_multiscale_internal(FLIF16RangeCoder *rc,
+                                                            FLIF16MultiscaleChanceContext *ctx,
+                                                            uint16_t type, uint8_t *target)
+{
+    if(!ff_flif16_rac_renorm(rc))
+        return 0; // EAGAIN condition
+    ff_flif16_rac_read_multiscale_symbol(rc, ctx, type, target);
+    return 1;
+}
+
+#define RAC_NZ_MULTISCALE_GET(rc, ctx, chance, target)                         \
+    if (!ff_flif16_rac_nz_read_multiscale_internal((rc), (ctx), (chance),      \
+                                                   (uint8_t *) (target))) {    \
+        goto need_more_data;                                                   \
+    }
+
+int ff_flif16_rac_read_nz_multiscale_int(FLIF16RangeCoder *rc,
+                                         FLIF16MultiscaleChanceContext *ctx,
+                                         int min, int max, int *target)
+{
+    uint8_t temp = 0;
+
+    if (min == max) {
+        *target = min;
+        rc->active = 0;
+        return 1;
+    }
+
+    if (!rc->active) {
+        rc->segment = 0;
+        rc->amin    = 1;
+        rc->active  = 1;
+        rc->sign    = 0;
+        rc->have    = 0;
+    }
+
+    switch (rc->segment) {
+    case 0:
+        RAC_NZ_MULTISCALE_GET(rc, ctx, NZ_INT_ZERO, &temp);
+        if (temp) {
+            *target = 0;
+            rc->active = 0;
+            return 1;
+        }
+        rc->segment++;
+
+    case 1:
+        if (min < 0) {
+            if (max > 0) {
+                RAC_NZ_MULTISCALE_GET(rc, ctx, NZ_INT_SIGN, &rc->sign);
+            } else {
+                rc->sign = 0;
+            }
+        } else {
+            rc->sign = 1;
+        }
+        rc->amax = (rc->sign ? max : -min);
+        rc->emax = ff_log2(rc->amax);
+        rc->e    = ff_log2(rc->amin);
+        rc->segment++;
+
+    case 2:
+        for (; rc->e < rc->emax; rc->e++) {
+            RAC_NZ_MULTISCALE_GET(rc, ctx, NZ_INT_EXP(((rc->e << 1) + rc->sign)),
+                       &temp);
+            if (temp)
+                break;
+            temp = 0;
+        }
+        rc->have = (1 << rc->e);
+        rc->left = rc->have - 1;
+        rc->pos  = rc->e;
+        rc->segment++;
+
+        while (rc->pos > 0) {
+            rc->pos--;
+            rc->left >>= 1;
+            rc->minabs1 = rc->have | (1 << rc->pos);
+            rc->maxabs0 = rc->have | rc->left;
+
+            if (rc->minabs1 > rc->amax) {
+                continue;
+            } else if (rc->maxabs0 >= rc->amin) {
+    case 3:
+                RAC_NZ_MULTISCALE_GET(rc, ctx, NZ_INT_MANT(rc->pos), &temp);
+                if (temp)
+                    rc->have = rc->minabs1;
+                temp = 0;
+            } else
+                rc->have = rc->minabs1;
+        }
+    }
+
+    *target = (rc->sign ? rc->have : -rc->have);
+    rc->active = 0;
+    return 1;
+
+    need_more_data:
+    return 0;
+}
+
+int ff_flif16_rac_read_gnz_multiscale_int(FLIF16RangeCoder *rc,
+                                          FLIF16MultiscaleChanceContext *ctx,
+                                          int min, int max, int *target)
+{
+    int ret;
+    if (min > 0) {
+        ret = ff_flif16_rac_read_nz_multiscale_int(rc, ctx, 0, max - min, target);
+        if (ret)
+            *target += min;
+    } else if (max < 0) {
+        ret =  ff_flif16_rac_read_nz_multiscale_int(rc, ctx, min - max, 0, target);
+        if (ret)
+            *target += max;
+    } else
+        ret = ff_flif16_rac_read_nz_multiscale_int(rc, ctx, min, max, target);
+    return ret;
+
+}
+#endif
+
+/*
+ * Ported from rangecoder.c.
+ * FLIF's reference decoder uses a slightly modified version of this function.
+ * The copyright of rangecoder.c is in 2004, and therefore this function counts
+ * as prior art to the function in the reference decoder (earliest copyright
+ * 2010.)
+ */
+static void build_table(uint16_t *zero_state, uint16_t *one_state, size_t size,
+                        uint32_t factor, unsigned int max_p)
+{
+    const int64_t one = 1LL << 32;
+    int64_t p = one / 2;
+    unsigned int last_p8 = 0, p8;
+    unsigned int i;
+
+    for (i = 0; i < size / 2; i++) {
+        p8 = (size * p + one / 2) >> 32;
+        if (p8 <= last_p8)
+            p8 = last_p8 + 1;
+        if (last_p8 && last_p8 < size && p8 <= max_p)
+            one_state[last_p8] = p8;
+        p += ((one - p) * factor + one / 2) >> 32;
+        last_p8 = p8;
+    }
+
+    for (i = size - max_p; i <= max_p; i++) {
+        if (one_state[i])
+            continue;
+        p = (i * one + size / 2) / size;
+        p += ((one - p) * factor + one / 2) >> 32;
+        p8 = (size * p + one / 2) >> 32; // FIXME try without the one
+        if (p8 <= i)
+            p8 = i + 1;
+        if (p8 > max_p)
+            p8 = max_p;
+        one_state[i] = p8;
+    }
+
+    for (i = 1; i < size; i++)
+        zero_state[i] = size - one_state[size - i];
+}
+
+static inline uint32_t log4k_compute(int32_t x, uint32_t base)
+{
+    int bits     = 8 * sizeof(int32_t) - ff_clz(x);
+    uint64_t y   = ((uint64_t) x) << (32 - bits);
+    uint32_t res = base * (13 - bits);
+    uint32_t add = base;
+    for (; (add > 1) && ((y & 0x7FFFFFFF) != 0);
+           y = (((uint64_t) y) * y + 0x40000000) >> 31,
+           add >>= 1)
+        if ((y >> 32)) {
+            res -= add;
+            y >>= 1;
+        }
+    return res;
+}
+
+void ff_flif16_build_log4k_table(FLIF16Log4kTable *log4k)
+{
+    log4k->table[0] = 0;
+    for (int i = 1; i < 4096; i++)
+        log4k->table[i] = (log4k_compute(i, (65535UL << 16) / 12) +
+                          (1 << 15)) >> 16;
+    log4k->scale = 65535 / 12;
+}
+
+void ff_flif16_chancetable_init(FLIF16ChanceTable *ct, int alpha, int cut)
+{
+    build_table(ct->zero_state, ct->one_state, 4096, alpha, 4096 - cut);
+}
+
+void ff_flif16_chancecontext_init(FLIF16ChanceContext *ctx)
+{
+    memcpy(&ctx->data, &flif16_nz_int_chances, sizeof(flif16_nz_int_chances));
+}
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+FLIF16MultiscaleChanceTable *ff_flif16_multiscale_chancetable_init(void)
+{
+    unsigned int len = MULTISCALE_CHANCETABLE_DEFAULT_SIZE;
+    FLIF16MultiscaleChanceTable *ct = av_malloc(sizeof(*ct));
+    if (!ct)
+        return NULL;
+    for (int i = 0; i < len; i++) {
+        ff_flif16_chancetable_init(&ct->sub_table[i],
+                                   flif16_multiscale_alphas[i],
+                                   MULTISCALE_CHANCETABLE_DEFAULT_CUT);
+    }
+    return ct;
+}
+
+/**
+ * Allocate and set all chances according to flif16_nz_int_chances
+ */
+void ff_flif16_multiscale_chancecontext_init(FLIF16MultiscaleChanceContext *ctx)
+{
+    for (int i = 0; i < FF_ARRAY_ELEMS(flif16_nz_int_chances); i++)
+        ff_flif16_multiscale_chance_set(&ctx->data[i], flif16_nz_int_chances[i]);
+}
+
+#endif
+
+int ff_flif16_read_maniac_tree(FLIF16RangeCoder *rc, FLIF16MANIACContext *m,
+                               FLIF16MinMax *prop_ranges,
+                               unsigned int prop_ranges_size,
+                               unsigned int channel)
+{
+    int oldp = 0, p = 0, split_val = 0, temp;
+    switch (rc->segment2) {
+    default: case 0:
+        rc->segment2 = 0;
+        if (!(m->forest[channel])) {
+            m->forest[channel] = av_mallocz(sizeof(*(m->forest[channel])));
+            if (!(m->forest[channel]))
+                return AVERROR(ENOMEM);
+            m->forest[channel]->data  = av_mallocz_array(MANIAC_TREE_BASE_SIZE,
+                                                         sizeof(*(m->forest[channel]->data)));
+            if (!m->forest[channel]->data)
+                return AVERROR(ENOMEM);
+            m->stack = av_malloc_array(MANIAC_TREE_BASE_SIZE, sizeof(*(m->stack)));
+            if (!(m->stack))
+                return AVERROR(ENOMEM);
+
+            for (int i = 0; i < 3; i++) {
+#ifdef MULTISCALE_CHANCES_ENABLED
+                ff_flif16_multiscale_chancecontext_init(&m->ctx[i]);
+#else
+                ff_flif16_chancecontext_init(&m->ctx[i]);
+#endif
+            }
+            m->stack_top = m->tree_top = 0;
+
+            m->forest[channel]->size       = MANIAC_TREE_BASE_SIZE;
+            m->stack_size                  = MANIAC_TREE_BASE_SIZE;
+
+            m->stack[m->stack_top].id      = m->tree_top;
+            m->stack[m->stack_top].mode    = 0;
+            m->stack[m->stack_top].visited = 0;
+            m->stack[m->stack_top].p       = 0;
+
+            m->stack_top++;
+            m->tree_top++;
+        }
+        rc->segment2++;
+
+    case 1:
+        while (m->stack_top) {
+            oldp = m->stack[m->stack_top - 1].p;
+            if (!m->stack[m->stack_top - 1].visited) {
+                switch (m->stack[m->stack_top - 1].mode) {
+                case 1:
+                    prop_ranges[oldp].min = m->stack[m->stack_top - 1].min;
+                    prop_ranges[oldp].max = m->stack[m->stack_top - 1].max;
+                    break;
+
+                case 2:
+                    prop_ranges[oldp].min = m->stack[m->stack_top - 1].min;
+                    break;
+                }
+            } else {
+                prop_ranges[oldp].max = m->stack[m->stack_top - 1].max2;
+                m->stack_top--;
+                rc->segment2 = 1;
+                continue;
+            }
+            m->stack[m->stack_top - 1].visited = 1;
+            rc->segment2++;
+
+    case 2:
+            RAC_GET(rc, &m->ctx[0], 0, prop_ranges_size,
+                    &m->forest[channel]->data[m->stack[m->stack_top - 1].id].property,
+                    FLIF16_RAC_MANIAC_GNZ_INT);
+            p = --(m->forest[channel]->data[m->stack[m->stack_top - 1].id].property);
+            if (p == -1) {
+                m->stack_top--;
+                rc->segment2 = 1;
+                continue;
+            }
+
+            m->forest[channel]->data[m->stack[m->stack_top - 1].id].child_id = m->tree_top;
+            rc->oldmin = prop_ranges[p].min;
+            rc->oldmax = prop_ranges[p].max;
+            if (rc->oldmin >= rc->oldmax)
+                return AVERROR_INVALIDDATA;
+            rc->segment2++;
+
+    case 3:
+            RAC_GET(rc, &m->ctx[1], MANIAC_TREE_MIN_COUNT, MANIAC_TREE_MAX_COUNT,
+                    &m->forest[channel]->data[m->stack[m->stack_top - 1].id].count,
+                    FLIF16_RAC_MANIAC_GNZ_INT);
+            rc->segment2++;
+
+    case 4:
+            RAC_GET(rc, &m->ctx[2], rc->oldmin, rc->oldmax - 1,
+                    &m->forest[channel]->data[m->stack[m->stack_top - 1].id].split_val,
+                    FLIF16_RAC_MANIAC_GNZ_INT);
+            split_val = m->forest[channel]->data[m->stack[m->stack_top - 1].id].split_val;
+            rc->segment2++;
+
+    case 5:
+            if ((m->tree_top + 2) >= m->forest[channel]->size) {
+                m->forest[channel]->data = av_realloc_f(m->forest[channel]->data,
+                                                        m->forest[channel]->size * 2,
+                                                        sizeof(*(m->forest[channel]->data)));
+                if(!(m->forest[channel]->data))
+                    return AVERROR(ENOMEM);
+                m->forest[channel]->size *= 2;
+            }
+
+            if ((m->stack_top + 2) >= m->stack_size) {
+                m->stack = av_realloc_f(m->stack, m->stack_size * 2, sizeof(*(m->stack)));
+                if(!(m->stack))
+                    return AVERROR(ENOMEM);
+                m->stack_size *= 2;
+            }
+
+            temp = m->forest[channel]->data[m->stack[m->stack_top - 1].id].property;
+
+            // Parent
+            m->stack[m->stack_top - 1].p    = temp;
+            m->stack[m->stack_top - 1].max2 = rc->oldmax;
+
+            // Right child
+            m->stack[m->stack_top].id      = m->tree_top + 1;
+            m->stack[m->stack_top].p       = temp;
+            m->stack[m->stack_top].min     = rc->oldmin;
+            m->stack[m->stack_top].max     = split_val;
+            m->stack[m->stack_top].mode    = 1;
+            m->stack[m->stack_top].visited = 0;
+            m->stack_top++;
+
+            // Left Child
+            m->stack[m->stack_top].id      = m->tree_top;
+            m->stack[m->stack_top].p       = temp;
+            m->stack[m->stack_top].min     = split_val + 1;
+            m->stack[m->stack_top].mode    = 2;
+            m->stack[m->stack_top].visited = 0;
+            m->stack_top++;
+
+            m->tree_top += 2;
+            rc->segment2 = 1;
+        }
+    }
+
+    m->forest[channel]->data = av_realloc_f(m->forest[channel]->data,
+                                            m->tree_top,
+                                            sizeof(*m->forest[channel]->data));
+    if (!m->forest[channel]->data)
+        return AVERROR(ENOMEM);
+    m->forest[channel]->size = m->tree_top;
+    av_freep(&m->stack);
+    m->stack_top = 0;
+    rc->segment2 = 0;
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+void ff_flif16_maniac_close(FLIF16MANIACContext *m, uint8_t num_planes,
+                            uint8_t lookback)
+{
+    for (int i = 0; i < (lookback ? MAX_PLANES : num_planes); i++) {
+        if (!m->forest[i])
+            continue;
+        av_freep(&m->forest[i]->data);
+        av_freep(&m->forest[i]->leaves);
+        av_freep(&m->forest[i]);
+    }
+
+    av_freep(&m->forest);
+
+    // Should be already freed in MANIAC reading, but checking anyway.
+    av_freep(&m->stack);
+}
+
+
+static FLIF16MANIACChanceContext *ff_flif16_maniac_findleaf(FLIF16MANIACContext *m,
+                                                            uint8_t channel,
+                                                            int32_t *properties)
+{
+    unsigned int pos = 0;
+    uint32_t old_leaf;
+    uint32_t new_leaf;
+    FLIF16MANIACTree *tree = m->forest[channel];
+    FLIF16MANIACNode *nodes = tree->data;
+
+    if (!m->forest[channel]->leaves) {
+        m->forest[channel]->leaves = av_mallocz_array(MANIAC_TREE_BASE_SIZE,
+                                                      sizeof(*m->forest[channel]->leaves));
+        m->forest[channel]->leaves_size = MANIAC_TREE_BASE_SIZE;
+        if(!m->forest[channel]->leaves)
+            return NULL;
+#ifdef MULTISCALE_CHANCES_ENABLED
+        ff_flif16_multiscale_chancecontext_init(&m->forest[channel]->leaves[0]);
+#else
+        ff_flif16_chancecontext_init(&m->forest[channel]->leaves[0]);
+#endif
+        tree->leaves_top = 1;
+    }
+
+    while (nodes[pos].property != -1) {
+        if (nodes[pos].count < 0) {
+            if (properties[nodes[pos].property] > nodes[pos].split_val)
+                pos = nodes[pos].child_id;
+            else
+                pos = nodes[pos].child_id + 1;
+        } else if (nodes[pos].count > 0) {
+            nodes[pos].count--;
+            break;
+        } else {
+            nodes[pos].count--;
+            if ((tree->leaves_top) >= tree->leaves_size) {
+                m->forest[channel]->leaves = av_realloc_f(m->forest[channel]->leaves,
+                                                          m->forest[channel]->leaves_size * 2,
+                                                          sizeof(*m->forest[channel]->leaves));
+                if (!m->forest[channel]->leaves)
+                    return NULL;
+                m->forest[channel]->leaves_size *= 2;
+            }
+            old_leaf = nodes[pos].leaf_id;
+            new_leaf = tree->leaves_top;
+            memcpy(&m->forest[channel]->leaves[tree->leaves_top],
+                   &m->forest[channel]->leaves[nodes[pos].leaf_id],
+                   sizeof(*m->forest[channel]->leaves));
+            tree->leaves_top++;
+            nodes[nodes[pos].child_id].leaf_id = old_leaf;
+            nodes[nodes[pos].child_id + 1].leaf_id = new_leaf;
+
+            if (properties[nodes[pos].property] > nodes[pos].split_val)
+                return &m->forest[channel]->leaves[old_leaf];
+            else
+                return &m->forest[channel]->leaves[new_leaf];
+        }
+    }
+
+    return &m->forest[channel]->leaves[m->forest[channel]->data[pos].leaf_id];
+}
+
+int ff_flif16_maniac_read_int(FLIF16RangeCoder *rc, FLIF16MANIACContext *m,
+                              int32_t *properties, uint8_t channel,
+                              int min, int max, int *target)
+{
+    if (!rc->curr_leaf)
+        rc->segment2 = 0;
+
+    switch(rc->segment2) {
+    case 0:
+        if (min == max) {
+            *target = min;
+            goto end;
+        }
+        rc->curr_leaf = ff_flif16_maniac_findleaf(m, channel, properties);
+        if(!rc->curr_leaf) {
+            return AVERROR(ENOMEM);
+        }
+        rc->segment2++;
+
+    case 1:
+        RAC_GET(rc, rc->curr_leaf, min, max, target, FLIF16_RAC_MANIAC_NZ_INT);
+    }
+
+    end:
+    rc->curr_leaf = NULL;
+    rc->segment2  = 0;
+    return 1;
+
+    need_more_data:
+    return 0;
+}
diff --git a/libavcodec/flif16_rangecoder.h b/libavcodec/flif16_rangecoder.h
new file mode 100644
index 0000000000..649c42db53
--- /dev/null
+++ b/libavcodec/flif16_rangecoder.h
@@ -0,0 +1,400 @@
+/*
+ * Range coder for FLIF16
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Range coder for FLIF16.
+ */
+
+#ifndef AVCODEC_FLIF16_RANGECODER_H
+#define AVCODEC_FLIF16_RANGECODER_H
+
+#include "libavutil/mem.h"
+#include "libavutil/intmath.h"
+#include "bytestream.h"
+
+#include <stdint.h>
+
+
+#define FLIF16_RAC_MAX_RANGE_BITS 24
+#define FLIF16_RAC_MAX_RANGE_BYTES (FLIF16_RAC_MAX_RANGE_BITS / 8)
+#define FLIF16_RAC_MIN_RANGE_BITS 16
+#define FLIF16_RAC_MAX_RANGE (uint32_t) 1 << FLIF16_RAC_MAX_RANGE_BITS
+#define FLIF16_RAC_MIN_RANGE (uint32_t) 1 << FLIF16_RAC_MIN_RANGE_BITS
+
+#define CHANCETABLE_DEFAULT_ALPHA (0xFFFFFFFF / 19)
+#define CHANCETABLE_DEFAULT_CUT 2
+
+/*
+ * Enabling this option will make the decoder assume that the MANIAC tree
+ * (and subsequent pixeldata) has been encoded using the multiscale chance
+ * probability model. The other (simpler) model and this model ane non
+ * interchangable.
+ */
+
+// #define MULTISCALE_CHANCES_ENABLED
+
+#define MULTISCALE_CHANCETABLE_DEFAULT_SIZE 6
+#define MULTISCALE_CHANCETABLE_DEFAULT_CUT  8
+
+#define MANIAC_TREE_BASE_SIZE 1600
+#define MANIAC_TREE_MIN_COUNT 1
+#define MANIAC_TREE_MAX_COUNT 512
+
+typedef enum FLIF16RACReader {
+    FLIF16_RAC_BIT = 0,
+    FLIF16_RAC_UNI_INT8,
+    FLIF16_RAC_UNI_INT16,
+    FLIF16_RAC_UNI_INT32,
+    FLIF16_RAC_CHANCE,
+    FLIF16_RAC_NZ_INT,
+    FLIF16_RAC_GNZ_INT,
+#ifdef MULTISCALE_CHANCES_ENABLED
+    FLIF16_RAC_NZ_MULTISCALE_INT,
+    FLIF16_RAC_GNZ_MULTISCALE_INT,
+    FLIF16_RAC_MANIAC_NZ_INT = FLIF16_RAC_NZ_MULTISCALE_INT,
+    FLIF16_RAC_MANIAC_GNZ_INT = FLIF16_RAC_GNZ_MULTISCALE_INT,
+#else
+    FLIF16_RAC_MANIAC_NZ_INT = FLIF16_RAC_NZ_INT,
+    FLIF16_RAC_MANIAC_GNZ_INT = FLIF16_RAC_GNZ_INT,
+#endif
+} FLIF16RACReader;
+
+typedef struct FLIF16ChanceTable {
+    uint16_t zero_state[4096];
+    uint16_t one_state[4096];
+} FLIF16ChanceTable;
+
+typedef struct FLIF16MultiscaleChanceTable {
+    FLIF16ChanceTable sub_table[MULTISCALE_CHANCETABLE_DEFAULT_SIZE];
+} FLIF16MultiscaleChanceTable;
+
+
+typedef struct FLIF16Log4kTable {
+    int scale;
+    uint16_t table[4097];
+} FLIF16Log4kTable;
+
+
+/*
+ * Required by the multiscale chance probability model's algorithm.
+ */
+static const uint32_t flif16_multiscale_alphas[] = {
+    21590903, 66728412, 214748365, 7413105, 106514140, 10478104
+};
+
+typedef struct FLIF16MultiscaleChance {
+    uint16_t chances[MULTISCALE_CHANCETABLE_DEFAULT_SIZE];
+    uint32_t quality[MULTISCALE_CHANCETABLE_DEFAULT_SIZE];
+    uint8_t best;
+} FLIF16MultiscaleChance;
+
+static uint16_t flif16_nz_int_chances[] = {
+    1000,        // ZERO
+    2048,        // SIGN (0)  (1)
+    1000, 1000,  // EXP:  0,   1
+    1200, 1200,  // EXP:  2,   3
+    1500, 1500,  // EXP:  4,   5
+    1750, 1750,  // EXP:  6,   7
+    2000, 2000,  // EXP:  8,   9
+    2300, 2300,  // EXP:  10,  11
+    2800, 2800,  // EXP:  12,  13
+    2400, 2400,  // EXP:  14,  15
+    2300, 2300,  // EXP:  16,  17
+    2048, 2048,  // EXP:  18,  19
+    2048, 2048,  // EXP:  20,  21
+    2048, 2048,  // EXP:  22,  23
+    2048, 2048,  // EXP:  24,  25
+    2048, 2048,  // EXP:  26,  27
+    2048, 2048,  // EXP:  28,  29
+    2048, 2048,  // EXP:  30,  31
+    2048, 2048,  // EXP:  32,  33
+    1900,        // MANT: 0
+    1850,        // MANT: 1
+    1800,        // MANT: 2
+    1750,        // MANT: 3
+    1650,        // MANT: 4
+    1600,        // MANT: 5
+    1600,        // MANT: 6
+    2048,        // MANT: 7
+    2048,        // MANT: 8
+    2048,        // MANT: 9
+    2048,        // MANT: 10
+    2048,        // MANT: 11
+    2048,        // MANT: 12
+    2048,        // MANT: 13
+    2048,        // MANT: 14
+    2048,        // MANT: 15
+    2048,        // MANT: 16
+    2048         // MANT: 17
+};
+
+#define NZ_INT_ZERO (0)
+#define NZ_INT_SIGN (1)
+#define NZ_INT_EXP(k) ((2 + (k)))
+#define NZ_INT_MANT(k) ((36 + (k)))
+
+
+typedef struct FLIF16MultiscaleChanceContext {
+    FLIF16MultiscaleChance data[FF_ARRAY_ELEMS(flif16_nz_int_chances)];
+} FLIF16MultiscaleChanceContext;
+
+// Maybe rename to symbol context
+typedef struct FLIF16ChanceContext {
+    uint16_t data[FF_ARRAY_ELEMS(flif16_nz_int_chances)];
+} FLIF16ChanceContext;
+
+typedef struct FLIF16MinMax {
+    int32_t min;
+    int32_t max;
+} FLIF16MinMax;
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+typedef FLIF16MultiscaleChanceContext FLIF16MANIACChanceContext;
+#else
+typedef FLIF16ChanceContext FLIF16MANIACChanceContext;
+#endif
+
+typedef struct FLIF16RangeCoder {
+    FLIF16ChanceTable ct;
+#ifdef MULTISCALE_CHANCES_ENABLED
+    FLIF16Log4kTable log4k;
+    FLIF16MultiscaleChanceTable *mct;
+#endif
+    void *bytestream; ///< Pointer to a PutByteContext or a GetByteContext
+                      ///  Struct
+    FLIF16MANIACChanceContext *curr_leaf;
+
+    uint_fast32_t range;
+    uint_fast32_t low;
+    uint8_t active;   ///< Is an integer reader currently active (to save/
+                      ///  transfer state)
+    uint8_t segment;  ///< The "segment" the function currently is in
+    uint8_t segment2;
+    uint8_t sign;
+
+    // uni_int state management
+    int32_t min;
+    int32_t len;
+    int32_t max; // Encoder
+    int32_t val; // Encoder
+
+    // nz_int state management
+    int amin, amax, emax, e, have, left, minabs1, maxabs0, pos;
+    int i, bit; // Encoder
+
+    // maniac_int state management
+    int oldmin, oldmax;
+
+    // encoder state management
+    int straddle_byte;
+    int straddle_count;
+} FLIF16RangeCoder;
+
+/**
+ * The Stack used to construct the MANIAC tree
+ */
+typedef struct FLIF16MANIACStack {
+    unsigned int id;
+    int p;
+    int min;
+    int max;
+    int max2;
+    uint8_t mode;
+    uint8_t visited;
+} FLIF16MANIACStack;
+
+typedef struct FLIF16MANIACNode {
+    int32_t property;
+    int32_t count;
+    int32_t split_val;
+    int32_t child_id;
+    int32_t leaf_id;
+} FLIF16MANIACNode;
+
+typedef struct FLIF16MANIACTree {
+    FLIF16MANIACNode *data;
+    FLIF16MANIACChanceContext *leaves;
+    unsigned int size;
+    unsigned int leaves_size;
+    unsigned int leaves_top;
+} FLIF16MANIACTree;
+
+typedef struct FLIF16MANIACContext {
+    FLIF16MANIACChanceContext ctx[3];
+    FLIF16MANIACTree **forest;
+    FLIF16MANIACStack *stack;
+    unsigned int tree_top;
+    unsigned int stack_top;
+    unsigned int stack_size;
+} FLIF16MANIACContext;
+
+int ff_flif16_rac_init(FLIF16RangeCoder *rc, GetByteContext *gb);
+
+void ff_flif16_rac_free(FLIF16RangeCoder *rc);
+
+uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc, uint8_t *target);
+
+uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc,
+                                   uint64_t b12, uint8_t *target);
+
+int ff_flif16_rac_read_uni_int(FLIF16RangeCoder *rc, int min, int len,
+                               void *target, int type);
+
+int ff_flif16_rac_read_nz_int(FLIF16RangeCoder *rc, FLIF16ChanceContext *ctx,
+                              int min, int max, int *target);
+
+int ff_flif16_rac_read_gnz_int(FLIF16RangeCoder *rc, FLIF16ChanceContext *ctx,
+                               int min, int max, int *target);
+
+void ff_flif16_chancecontext_init(FLIF16ChanceContext *ctx);
+
+void ff_flif16_chancetable_init(FLIF16ChanceTable *ct, int alpha, int cut);
+
+void ff_flif16_build_log4k_table(FLIF16Log4kTable *log4k);
+
+int ff_flif16_read_maniac_tree(FLIF16RangeCoder *rc, FLIF16MANIACContext *m,
+                               FLIF16MinMax *prop_ranges,
+                               unsigned int prop_ranges_size,
+                               unsigned int channel);
+
+void ff_flif16_maniac_close(FLIF16MANIACContext *m, uint8_t num_planes,
+                            uint8_t lookback);
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+
+void ff_flif16_multiscale_chancecontext_init(FLIF16MultiscaleChanceContext *ctx);
+
+FLIF16MultiscaleChanceTable *ff_flif16_multiscale_chancetable_init(void);
+
+int ff_flif16_rac_read_nz_multiscale_int(FLIF16RangeCoder *rc,
+                                         FLIF16MultiscaleChanceContext *ctx,
+                                         int min, int max, int *target);
+
+int ff_flif16_rac_read_gnz_multiscale_int(FLIF16RangeCoder *rc,
+                                          FLIF16MultiscaleChanceContext *ctx,
+                                          int min, int max, int *target);
+
+#endif
+
+int ff_flif16_maniac_read_int(FLIF16RangeCoder *rc, FLIF16MANIACContext *m,
+                              int32_t *properties, uint8_t channel,
+                              int min, int max, int *target);
+
+static inline int ff_flif16_rac_renorm(FLIF16RangeCoder *rc)
+{
+    while (rc->range <= FLIF16_RAC_MIN_RANGE) {
+        if (!bytestream2_get_bytes_left(rc->bytestream))
+            return 0;
+        rc->low <<= 8;
+        rc->range <<= 8;
+        rc->low |= bytestream2_get_byte(rc->bytestream);
+    }
+    return 1;
+}
+
+/**
+ * Reads an integer encoded by FLIF's RAC.
+ * @param[in]  val1 A generic value, chosen according to the required type
+ * @param[in]  val2 Same as val1
+ * @param[out] target The place where the resultant value should be written to
+ * @param[in]  type The type of the integer to be decoded specified by
+ *             FLIF16RACTypes
+ * @return     0 on bytestream empty, 1 on successful decoding.
+ */
+
+static inline int ff_flif16_rac_process(FLIF16RangeCoder *rc,
+                                        void *ctx, int val1, int val2,
+                                        void *target, int type)
+{
+    int flag = 0;
+    while (!flag) {
+        if (!ff_flif16_rac_renorm(rc)) {
+            return 0; // EAGAIN condition
+        }
+
+        switch (type) {
+        case FLIF16_RAC_BIT:
+            flag = ff_flif16_rac_read_bit(rc, (uint8_t *) target);
+            break;
+
+        case FLIF16_RAC_UNI_INT8:
+        case FLIF16_RAC_UNI_INT16:
+        case FLIF16_RAC_UNI_INT32:
+            flag = ff_flif16_rac_read_uni_int(rc, val1, val2, target, type);
+            break;
+
+        case FLIF16_RAC_CHANCE:
+            flag = ff_flif16_rac_read_chance(rc, val1, (uint8_t *) target);
+            break;
+
+        case FLIF16_RAC_NZ_INT:
+            flag = ff_flif16_rac_read_nz_int(rc, (FLIF16ChanceContext *) ctx,
+                                             val1, val2, (int *) target);
+            break;
+
+        case FLIF16_RAC_GNZ_INT:
+            flag = ff_flif16_rac_read_gnz_int(rc, (FLIF16ChanceContext *) ctx,
+                                              val1, val2, (int *) target);
+            break;
+#ifdef MULTISCALE_CHANCES_ENABLED
+        case FLIF16_RAC_NZ_MULTISCALE_INT:
+            flag = ff_flif16_rac_read_nz_multiscale_int(rc, (FLIF16MultiscaleChanceContext *) ctx,
+                                                        val1, val2, (int *) target);
+            break;
+
+        case FLIF16_RAC_GNZ_MULTISCALE_INT:
+            flag = ff_flif16_rac_read_gnz_multiscale_int(rc, (FLIF16MultiscaleChanceContext *) ctx,
+                                                         val1, val2, (int *) target);
+            break;
+#endif
+        }
+    }
+    return 1;
+}
+
+/**
+ * Macro meant to handle intermittent bytestreams with slightly more
+ * convenience. Requires a "need_more_data" label to be present in the given
+ * scope.
+ */
+#define RAC_GET(rc, ctx, val1, val2, target, type) \
+    if (!ff_flif16_rac_process((rc), (ctx), (val1), (val2), (target), (type))) { \
+        goto need_more_data; \
+    }
+
+/**
+ * Macro meant to handle intermittent bytestreams with slightly more
+ * convenience. Requires a "need_more_data" label to be present in the given
+ * scope.
+ * This macro will trigger a return with an integer value if an error occurs.
+ */
+#define MANIAC_GET(rc, m, prop, channel, min, max, target) \
+    { \
+        int ret = ff_flif16_maniac_read_int((rc), (m), (prop), (channel), (min), (max), (target)); \
+        if (ret < 0) { \
+            return ret; \
+        } else if (!ret) { \
+            goto need_more_data; \
+        } \
+    }
+
+#endif /* FLIF16_RANGECODER_H */
diff --git a/libavcodec/flif16_transform.c b/libavcodec/flif16_transform.c
new file mode 100644
index 0000000000..4b8716519f
--- /dev/null
+++ b/libavcodec/flif16_transform.c
@@ -0,0 +1,2895 @@
+/*
+ * Transforms for FLIF16
+ * Copyright (c) 2020 Kartik K. Khullar <kartikkhullar840 at gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Transforms for FLIF16
+ */
+
+#include "flif16_transform.h"
+#include "flif16_rangecoder.h"
+#include "libavutil/common.h"
+
+// Transform private structs
+
+typedef struct TransformPrivYCoCg {
+    FLIF16RangesContext *r_ctx;
+    int origmax4;
+} TransformPrivYCoCg;
+
+typedef struct TransformPrivPermuteplanes {
+    uint8_t subtract;
+    uint8_t permutation[5];
+    FLIF16RangesContext *r_ctx;
+    uint8_t from[4], to[4];
+    FLIF16ChanceContext ctx_a;
+} TransformPrivPermuteplanes;
+
+typedef struct TransformPrivChannelcompact {
+    FLIF16ChanceContext ctx_a;
+    size_t cpalette_size[4];
+    FLIF16ColorVal *cpalette[4];
+    FLIF16ColorVal *cpalette_inv[4];
+    FLIF16ColorVal min;
+    unsigned int cpalette_inv_size[4];
+    int remaining;
+    unsigned int i; // Iterator for nested loop.
+} TransformPrivChannelcompact;
+
+typedef struct TransformPrivBounds {
+    FLIF16ChanceContext ctx_a;
+    FLIF16ColorVal (*bounds)[2];
+    int min;
+} TransformPrivBounds;
+
+typedef struct TransformPrivPalette {
+    FLIF16ChanceContext ctx;
+    FLIF16ChanceContext ctxY;
+    FLIF16ChanceContext ctxI;
+    FLIF16ChanceContext ctxQ;
+    FLIF16ColorVal (*palette)[3];
+    FLIF16ColorVal min[3], max[3];
+    FLIF16ColorVal *prev;
+    FLIF16ColorVal pp[2];
+    FLIF16ColorVal Y, I, Q;
+    long unsigned size;
+    unsigned int p; // Iterator
+    int32_t max_palette_size;
+    uint8_t has_alpha;
+    uint8_t ordered_palette;
+    uint8_t sorted;
+} TransformPrivPalette;
+
+typedef struct TransformPrivPalettealpha {
+    FLIF16ChanceContext ctx;
+    FLIF16ChanceContext ctxY;
+    FLIF16ChanceContext ctxI;
+    FLIF16ChanceContext ctxQ;
+    FLIF16ChanceContext ctxA;
+    FLIF16ColorVal (*palette)[4];
+    FLIF16ColorVal min[4], max[4];
+    FLIF16ColorVal *prev;
+    FLIF16ColorVal pp[2];
+    FLIF16ColorVal Y, I, Q, A;
+    long unsigned int size;
+    unsigned int max_palette_size;
+    unsigned int p;
+    uint8_t alpha_zero_special;
+    uint8_t ordered_palette;
+    uint8_t already_has_palette;
+    uint8_t sorted;
+} TransformPrivPalettealpha;
+
+typedef int16_t ColorValCB;
+typedef struct ColorValCB_list ColorValCB_list ;
+
+typedef struct ColorValCB_list {
+    ColorValCB data;
+    ColorValCB_list *next;
+} ColorValCB_list;
+
+typedef struct ColorBucket {
+    ColorValCB *snapvalues;
+    ColorValCB_list *values;
+    ColorValCB_list *values_last;
+    ColorValCB min, max;
+    unsigned int snapvalues_size;
+    unsigned int values_size;
+    uint8_t discrete;
+} ColorBucket;
+
+typedef struct ColorBuckets {
+    ColorBucket bucket0;
+    ColorBucket bucket3;
+    ColorBucket empty_bucket;
+    ColorBucket *bucket1;
+    ColorBucket **bucket2; // List of a list
+    FLIF16RangesContext *ranges;
+    unsigned int bucket1_size;
+    unsigned int bucket2_size, bucket2_list_size;
+    int min0, min1;
+
+    /*
+     *  Data members used while reading buckets
+     */
+    unsigned int i, i2; // Iterator
+    FLIF16ColorVal smin, smax;
+    FLIF16ColorVal v;
+    int nb;
+} ColorBuckets;
+
+typedef struct TransformPrivColorbuckets {
+    FLIF16ChanceContext ctx[6];
+    ColorBuckets *cb;
+    FLIF16ColorVal pixelL[2], pixelU[2];
+    int i, j, k; // Iterators
+    uint8_t really_used;
+} TransformPrivColorbuckets;
+
+typedef struct TransformPrivFramedup {
+    FLIF16ChanceContext chancectx;
+    int *seen_before;
+    unsigned int i;
+    uint32_t nb;
+} TransformPrivFramedup;
+
+typedef struct TransformPrivFrameshape {
+    FLIF16ChanceContext chancectx;
+    int *b, *e; // Begin and end
+    uint32_t cols;
+    uint32_t nb;
+    unsigned int i;
+} TransformPrivFrameshape;
+
+typedef struct TransformPrivFramecombine {
+    FLIF16ChanceContext chancectx;
+    int max_lookback;
+    int user_max_lookback;
+    int nb_frames;
+    uint8_t was_flat;
+    uint8_t was_greyscale;
+    uint8_t orig_num_planes;
+} TransformPrivFramecombine;
+
+// Ranges private structs
+
+typedef struct RangesPrivChannelcompact {
+    int nb_colors[4];
+} RangesPrivChannelcompact;
+
+typedef struct RangesPrivYCoCg {
+    FLIF16RangesContext *r_ctx;
+    int origmax4;
+} RangesPrivYCoCg;
+
+typedef struct RangesPrivPermuteplanes {
+    FLIF16RangesContext *r_ctx;
+    uint8_t permutation[5];
+} RangesPrivPermuteplanes;
+
+typedef struct RangesPrivBounds {
+    FLIF16ColorVal (*bounds)[2];
+    FLIF16RangesContext *r_ctx;
+} RangesPrivBounds;
+
+typedef struct RangesPrivPalette {
+    FLIF16RangesContext *r_ctx;
+    int nb_colors;
+} RangesPrivPalette;
+
+typedef struct RangesPrivColorbuckets {
+    FLIF16RangesContext *r_ctx;
+    ColorBuckets *buckets;
+} RangesPrivColorbuckets;
+
+typedef struct RangesPrivFramecombine {
+    FLIF16RangesContext *ranges;
+    FLIF16ColorVal num_prev_frames;
+    FLIF16ColorVal alpha_min;
+    FLIF16ColorVal alpha_max;
+} RangesPrivFramecombine;
+
+typedef struct RangesPrivStatic {
+    FLIF16ColorVal (*bounds)[2];
+} RangesPrivStatic;
+
+/*
+ * =============================================================================
+ * Ranges
+ * =============================================================================
+ */
+
+/*
+ * Static
+ */
+
+static FLIF16ColorVal ff_static_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivStatic *data = r_ctx->priv_data;
+    av_assert1(p < r_ctx->num_planes);
+    return data->bounds[p][0];
+}
+
+static FLIF16ColorVal ff_static_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivStatic *data = r_ctx->priv_data;
+    av_assert1(p < r_ctx->num_planes);
+    return data->bounds[p][1];
+}
+
+static void ff_static_minmax(FLIF16RangesContext *src_ctx ,const int p,
+                             FLIF16ColorVal *prev_planes,
+                             FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    *minv = ff_flif16_ranges_min(src_ctx, p);
+    *maxv = ff_flif16_ranges_max(src_ctx, p);
+}
+
+static void ff_static_snap(FLIF16RangesContext *src_ctx , const int p,
+                           FLIF16ColorVal *prev_planes,
+                           FLIF16ColorVal *minv, FLIF16ColorVal *maxv,
+                           FLIF16ColorVal *v)
+{
+    ff_flif16_ranges_minmax(src_ctx, p, prev_planes, minv, maxv);
+    *maxv = FFMAX(*minv, *maxv);
+    *v = av_clip(*v, *minv, *maxv);
+}
+
+static void ff_static_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivStatic *data = r_ctx->priv_data;
+    av_freep(&data->bounds);
+}
+
+/*
+ * ChannelCompact
+ */
+
+static FLIF16ColorVal ff_channelcompact_min(FLIF16RangesContext *ranges, int p)
+{
+    return 0;
+}
+
+static FLIF16ColorVal ff_channelcompact_max(FLIF16RangesContext *src_ctx, int p)
+{
+    RangesPrivChannelcompact *data = src_ctx->priv_data;
+    return data->nb_colors[p];
+}
+
+static void ff_channelcompact_minmax(FLIF16RangesContext *r_ctx, int p,
+                                     FLIF16ColorVal *prev_planes,
+                                     FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivChannelcompact *data = r_ctx->priv_data;
+    *minv = 0;
+    *maxv = data->nb_colors[p];
+}
+
+/*
+ * YCoCg
+ */
+
+static inline FLIF16ColorVal ff_get_max_y(int origmax4)
+{
+    return 4 * origmax4 - 1;
+}
+
+static inline int ff_get_min_co(int origmax4, int yval)
+{
+    if (yval < origmax4 - 1)
+        return (- 3) - (4 * yval);
+    else if (yval >= 3 * origmax4)
+        return 4 * (1 + yval - (4 * origmax4));
+    else
+        return (- 4) * origmax4 + 1;
+}
+
+static inline int ff_get_max_co(int origmax4, int yval)
+{
+    if (yval < origmax4 - 1)
+        return 3 + 4 * yval;
+    else if (yval >= 3 * origmax4)
+        return 4 * origmax4 - 4 * (1 + yval - 3 * origmax4);
+    else
+        return 4 * origmax4 - 1;
+}
+
+static inline int ff_get_min_cg(int origmax4, int yval, int coval)
+{
+    if (yval < origmax4 - 1)
+        return -(2 * yval + 1);
+    else if (yval >= 3 * origmax4)
+        return -(2 * (4 * origmax4 - 1 - yval) - ((1 + abs(coval)) / 2) * 2);
+    else {
+        return -FFMIN(2 * origmax4 - 1 + (yval - origmax4 + 1) * 2,
+                      2 * origmax4 + (3 * origmax4 - 1 - yval) * 2 - ((1 + abs(coval)) / 2)*2);
+    }
+}
+
+static inline int ff_get_max_cg(int origmax4, int yval, int coval)
+{
+    if (yval < origmax4 - 1)
+        return 1 + 2 * yval - 2 * (abs(coval) / 2);
+    else if (yval >= 3 * origmax4)
+        return 2 * (4*origmax4 - 1 - yval);
+    else
+        return -FFMAX(- 4 * origmax4 + (1 + yval - 2 * origmax4) * 2,
+                      - 2 * origmax4 - (yval - origmax4) * 2 - 1 + (abs(coval) / 2) * 2);
+}
+
+static FLIF16ColorVal ff_ycocg_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivYCoCg *data = r_ctx->priv_data;
+    switch (p) {
+    case FLIF16_PLANE_Y:
+        return 0;
+    case FLIF16_PLANE_CO:
+    case FLIF16_PLANE_CG:
+        return -4 * data->origmax4 + 1;
+    default:
+        return ff_flif16_ranges_min(data->r_ctx, p);
+    }
+}
+
+static FLIF16ColorVal ff_ycocg_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivYCoCg *data = r_ctx->priv_data;
+    switch (p) {
+    case FLIF16_PLANE_Y:
+    case FLIF16_PLANE_CO:
+    case FLIF16_PLANE_CG:
+        return 4 * data->origmax4 - 1;
+    default:
+        return ff_flif16_ranges_max(data->r_ctx, p);
+    }
+}
+
+static void ff_ycocg_minmax(FLIF16RangesContext *r_ctx ,const int p,
+                            FLIF16ColorVal *prev_planes,
+                            FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivYCoCg *data = r_ctx->priv_data;
+    switch (p) {
+    case FLIF16_PLANE_Y:
+        *minv = 0;
+        *maxv = ff_get_max_y(data->origmax4);
+        break;
+    case FLIF16_PLANE_CO:
+        *minv = ff_get_min_co(data->origmax4, prev_planes[0]);
+        *maxv = ff_get_max_co(data->origmax4, prev_planes[0]);
+        break;
+    case FLIF16_PLANE_CG:
+        *minv = ff_get_min_cg(data->origmax4, prev_planes[0], prev_planes[1]);
+        *maxv = ff_get_max_cg(data->origmax4, prev_planes[0], prev_planes[1]);
+        break;
+    default:
+        ff_flif16_ranges_minmax(data->r_ctx, p, prev_planes, minv, maxv);
+    }
+}
+
+static void ff_ycocg_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivYCoCg *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no];
+    if (range->close)
+        range->close(data->r_ctx);
+    av_freep(&data->r_ctx->priv_data);
+    av_freep(&data->r_ctx);
+}
+
+/*
+ * PermutePlanesSubtract
+ */
+
+static FLIF16ColorVal ff_permuteplanessubtract_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    if (p == 0 || p > 2)
+        return ff_flif16_ranges_min(data->r_ctx, data->permutation[p]);
+    return ff_flif16_ranges_min(data->r_ctx, data->permutation[p]) -
+           ff_flif16_ranges_max(data->r_ctx, data->permutation[0]);
+}
+
+static FLIF16ColorVal ff_permuteplanessubtract_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    if (p == 0 || p > 2)
+        return ff_flif16_ranges_max(data->r_ctx, data->permutation[p]);
+    return ff_flif16_ranges_max(data->r_ctx, data->permutation[p]) -
+           ff_flif16_ranges_min(data->r_ctx, data->permutation[0]);
+}
+
+static void ff_permuteplanessubtract_minmax(FLIF16RangesContext *r_ctx, int p,
+                                            FLIF16ColorVal *prev_planes,
+                                            FLIF16ColorVal *minv,
+                                            FLIF16ColorVal *maxv)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    if (p == 0 || p > 2) {
+        *minv = ff_flif16_ranges_min(data->r_ctx, p);
+        *maxv = ff_flif16_ranges_max(data->r_ctx, p);
+    } else {
+        *minv = ff_flif16_ranges_min(data->r_ctx, data->permutation[p]) - prev_planes[0];
+        *maxv = ff_flif16_ranges_max(data->r_ctx, data->permutation[p]) - prev_planes[0];
+    }
+}
+
+/*
+ * PermutePlanes
+ */
+
+static FLIF16ColorVal ff_permuteplanes_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    return ff_flif16_ranges_min(data->r_ctx, data->permutation[p]);
+}
+
+static FLIF16ColorVal ff_permuteplanes_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    return ff_flif16_ranges_max(data->r_ctx, data->permutation[p]);
+}
+
+static void ff_permuteplanes_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no];
+    if (range->close)
+        range->close(data->r_ctx);
+    av_freep(&data->r_ctx->priv_data);
+    av_freep(&data->r_ctx);
+}
+
+/*
+ * Bounds
+ */
+
+static FLIF16ColorVal ff_bounds_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    av_assert1(p < r_ctx->num_planes);
+    return FFMAX(ff_flif16_ranges_min(data->r_ctx, p), data->bounds[p][0]);
+}
+
+static FLIF16ColorVal ff_bounds_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    av_assert1(p < r_ctx->num_planes);
+    return FFMIN(ff_flif16_ranges_max(data->r_ctx, p), data->bounds[p][1]);
+}
+
+static void ff_bounds_minmax(FLIF16RangesContext *r_ctx, int p,
+                             FLIF16ColorVal *prev_planes,
+                             FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    av_assert1(p < r_ctx->num_planes);
+    if (p == 0 || p == 3) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+        return;
+    }
+    ff_flif16_ranges_minmax(data->r_ctx, p, prev_planes, minv, maxv);
+    *minv = FFMAX(*minv, data->bounds[p][0]);
+    *maxv = FFMIN(*maxv, data->bounds[p][1]);
+    if (*minv > *maxv) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+    }
+    av_assert1(*minv <= *maxv);
+}
+
+static void ff_bounds_snap(FLIF16RangesContext *r_ctx, int p,
+                           FLIF16ColorVal *prev_planes, FLIF16ColorVal *minv,
+                           FLIF16ColorVal *maxv, FLIF16ColorVal *v)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    if (p == 0 || p == 3) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+    } else {
+        ff_flif16_ranges_snap(data->r_ctx, p, prev_planes, minv, maxv, v);
+        *minv = FFMAX(*minv, data->bounds[p][0]);
+        *maxv = FFMIN(*maxv, data->bounds[p][1]);
+        if (*minv > *maxv) {
+            *minv = data->bounds[p][0];
+            *maxv = data->bounds[p][1];
+        }
+    }
+    *v = av_clip(*v, *minv, *maxv);
+}
+
+static void ff_bounds_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no];
+    if (range->close)
+        range->close(data->r_ctx);
+    av_freep(&data->r_ctx->priv_data);
+    av_freep(&data->bounds);
+    av_freep(&data->r_ctx);
+}
+
+/*
+ * Palette
+ */
+
+static FLIF16ColorVal ff_palette_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    if (p < 3)
+        return 0;
+    else
+        return ff_flif16_ranges_min(data->r_ctx, p);
+}
+
+static FLIF16ColorVal ff_palette_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    if (p == 1)
+        return data->nb_colors-1;
+    else if (p < 3)
+        return 0;
+    else
+        return ff_flif16_ranges_max(data->r_ctx, p);
+}
+
+static void ff_palette_minmax(FLIF16RangesContext *r_ctx, int p,
+                              FLIF16ColorVal *prev_planes,
+                              FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    if (p == FLIF16_PLANE_CO) {
+        *minv = 0;
+        *maxv = data->nb_colors-1;
+    }
+    else if (p < FLIF16_PLANE_ALPHA) {
+        *minv = 0;
+        *maxv = 0;
+    }
+    else
+        ff_flif16_ranges_minmax(data->r_ctx, p, prev_planes, minv, maxv);
+}
+
+static void ff_palette_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no];
+    if (range->close)
+        range->close(data->r_ctx);
+    av_freep(&data->r_ctx->priv_data);
+    av_freep(&data->r_ctx);
+}
+
+/*
+ * Palette Alpha
+ */
+
+static FLIF16ColorVal ff_palettealpha_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    if (p < FLIF16_PLANE_ALPHA)
+        return 0;
+    else if (p == FLIF16_PLANE_ALPHA)
+        return 1;
+    else
+        return ff_flif16_ranges_min(data->r_ctx, p);
+}
+
+static FLIF16ColorVal ff_palettealpha_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    switch (p) {
+    case FLIF16_PLANE_Y:
+        return 0;
+    case FLIF16_PLANE_CO:
+        return data->nb_colors-1;
+    case FLIF16_PLANE_CG:
+        return 0;
+    case FLIF16_PLANE_ALPHA:
+        return 1;
+    default:
+        return ff_flif16_ranges_max(data->r_ctx, p);
+    }
+}
+
+static void ff_palettealpha_minmax(FLIF16RangesContext *r_ctx, int p,
+                                   FLIF16ColorVal *prev_planes,
+                                   FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivPalette *data = r_ctx->priv_data;
+    if (p == FLIF16_PLANE_CO) {
+        *minv = 0;
+        *maxv = data->nb_colors - 1;
+    } else if (p < FLIF16_PLANE_ALPHA) {
+        *minv = 0;
+        *maxv = 0;
+    } else if (p == FLIF16_PLANE_ALPHA) {
+        *minv = 1;
+        *maxv = 1;
+    } else
+        ff_flif16_ranges_minmax(data->r_ctx, p, prev_planes, minv, maxv);
+}
+
+/*
+ * ColorBuckets
+ */
+
+static void ff_init_bucket_default(ColorBucket *b)
+{
+    b->min = 10000;
+    b->max = -10000;
+    b->discrete = 1;
+    b->values_size = 0;
+    b->snapvalues_size = 0;
+    b->snapvalues = NULL;
+    b->values = NULL;
+}
+
+static ColorBucket *ff_bucket_buckets2(ColorBuckets *buckets, const int p,
+                                       const FLIF16ColorVal *prev_planes)
+{
+    unsigned diff = prev_planes[0] - buckets->min0;
+    unsigned diff1;
+    av_assert0(p >= FLIF16_PLANE_Y);
+    av_assert0(p < FLIF16_PLANE_LOOKBACK);
+    if (p == FLIF16_PLANE_Y)
+        return &buckets->bucket0;
+    if (p == FLIF16_PLANE_CO) {
+        av_assert0(diff < buckets->bucket1_size);
+        return &buckets->bucket1[diff];
+    }
+    if (p == FLIF16_PLANE_CG) {
+        diff1 = (prev_planes[1] - buckets->min1) / 4;
+        av_assert0(diff < buckets->bucket2_size);
+        av_assert0(diff1 < buckets->bucket2_list_size);
+        return &buckets->bucket2[diff][diff1];
+    }
+
+    return &buckets->bucket3;
+}
+
+static ColorBucket *ff_bucket_buckets(ColorBuckets *buckets, const int p,
+                                      const FLIF16ColorVal *prev_planes)
+{
+    av_assert0(p >= 0);
+    av_assert0(p < 4);
+    if (p == FLIF16_PLANE_Y)
+        return &buckets->bucket0;
+    if (p == FLIF16_PLANE_CO) {
+        int i = (prev_planes[0] - buckets->min0);
+        // i is signed because following check is necessary for code flow.
+        if (i >= 0 && i < (int)buckets->bucket1_size)
+            return &buckets->bucket1[i];
+        else
+            return &buckets->empty_bucket;
+    }
+    if (p == FLIF16_PLANE_CG) {
+        int i = (prev_planes[0] - buckets->min0);
+        int j = (prev_planes[1] - buckets->min1) / 4;
+        if (i >= 0 && i < (int)buckets->bucket1_size &&
+            j >= 0 && j < (int) buckets->bucket2_list_size)
+            return &buckets->bucket2[i][j];
+        else
+            return &buckets->empty_bucket;
+    }
+
+    return &buckets->bucket3;
+}
+
+static FLIF16ColorVal ff_snap_color_bucket(ColorBucket *bucket, FLIF16ColorVal c)
+{
+    if (c <= bucket->min) {
+        return bucket->min;
+    }
+    if (c >= bucket->max) {
+        return bucket->max;
+    }
+    if (bucket->discrete) {
+        av_assert1((FLIF16ColorVal)bucket->snapvalues_size > (c - bucket->min));
+        return bucket->snapvalues[c - bucket->min];
+    }
+    return c;
+}
+
+static FLIF16ColorVal ff_colorbuckets_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivColorbuckets *data = r_ctx->priv_data;
+    return ff_flif16_ranges_min(data->r_ctx, p);
+}
+
+static FLIF16ColorVal ff_colorbuckets_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivColorbuckets *data = r_ctx->priv_data;
+    return ff_flif16_ranges_max(data->r_ctx, p);
+}
+
+static void ff_colorbuckets_snap(FLIF16RangesContext *src_ctx, const int p,
+                                 FLIF16ColorVal *prev_planes,
+                                 FLIF16ColorVal *minv, FLIF16ColorVal *maxv,
+                                 FLIF16ColorVal *v)
+{
+    RangesPrivColorbuckets *data = src_ctx->priv_data;
+    ColorBucket *b = ff_bucket_buckets(data->buckets, p, prev_planes);
+    *minv = b->min;
+    *maxv = b->max;
+    if (b->min > b->max) {
+        *minv = ff_colorbuckets_min(src_ctx, p);
+        *v = *minv;
+        *maxv = ff_colorbuckets_max(src_ctx, p);
+        return;
+    }
+    *v = ff_snap_color_bucket(b, *v);
+}
+
+static void ff_colorbuckets_minmax(FLIF16RangesContext *r_ctx,
+                                   int p, FLIF16ColorVal *prev_planes,
+                                   FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivColorbuckets *data = r_ctx->priv_data;
+    const ColorBucket *b = ff_bucket_buckets(data->buckets, p, prev_planes);
+    *minv = b->min;
+    *maxv = b->max;
+    if (b->min > b->max) {
+        *minv = ff_colorbuckets_min(r_ctx, p);
+        *maxv = ff_colorbuckets_max(r_ctx, p);
+    }
+}
+
+static void ff_list_close(ColorValCB_list *list)
+{
+    ColorValCB_list *temp;
+    while (list) {
+        temp = list;
+        list = list->next;
+        av_freep(&temp);
+    }
+}
+
+static void ff_priv_colorbuckets_close(ColorBuckets *cb)
+{
+    for (unsigned int i = 0; i < cb->bucket1_size; i++) {
+        if (cb->bucket1[i].snapvalues)
+            av_freep(&cb->bucket1[i].snapvalues);
+        if (cb->bucket1[i].values)
+            ff_list_close(cb->bucket1[i].values);
+    }
+    av_freep(&cb->bucket1);
+
+    if (cb->bucket0.snapvalues)
+        av_freep(&cb->bucket0.snapvalues);
+    if (cb->bucket0.values)
+        ff_list_close(cb->bucket0.values);
+
+    if (cb->bucket3.snapvalues)
+        av_freep(&cb->bucket3.snapvalues);
+    if (cb->bucket3.values)
+        ff_list_close(cb->bucket3.values);
+
+    for (unsigned int i = 0; i < cb->bucket2_size; i++) {
+        for (unsigned int j = 0; j < cb->bucket2_list_size; j++) {
+            if (cb->bucket2[i][j].snapvalues)
+                av_freep(&cb->bucket2[i][j].snapvalues);
+            if (cb->bucket2[i][j].values)
+                ff_list_close(cb->bucket2[i][j].values);
+        }
+        av_freep(&cb->bucket2[i]);
+    }
+    av_freep(&cb->bucket2);
+}
+
+static void ff_colorbuckets_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivColorbuckets *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no];
+    if (range->close)
+        range->close(data->r_ctx);
+    av_freep(&data->r_ctx->priv_data);
+    av_freep(&data->r_ctx);
+}
+
+static FLIF16ColorVal ff_framecombine_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivFramecombine *data = r_ctx->priv_data;
+    if (p < FLIF16_PLANE_ALPHA)
+        return ff_flif16_ranges_min(data->ranges, p);
+    else if (p == FLIF16_PLANE_ALPHA)
+        return data->alpha_min;
+    else
+        return 0;
+}
+
+static FLIF16ColorVal ff_framecombine_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivFramecombine *data = r_ctx->priv_data;
+    if (p < FLIF16_PLANE_ALPHA)
+        return ff_flif16_ranges_max(data->ranges, p);
+    else if (p == FLIF16_PLANE_ALPHA)
+        return data->alpha_max;
+    else
+        return data->num_prev_frames;
+}
+
+static void ff_framecombine_minmax(FLIF16RangesContext *r_ctx,
+                                   int p, FLIF16ColorVal *prev_planes,
+                                   FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    RangesPrivFramecombine *data = r_ctx->priv_data;
+    if (p >= 3) {
+        *minv = ff_framecombine_min(r_ctx, p);
+        *maxv = ff_framecombine_max(r_ctx, p);
+    } else
+        ff_flif16_ranges_minmax(data->ranges, p, prev_planes, minv, maxv);
+}
+
+static void ff_framecombine_snap(FLIF16RangesContext *src_ctx, const int p,
+                                 FLIF16ColorVal *prev_planes,
+                                 FLIF16ColorVal *minv, FLIF16ColorVal *maxv,
+                                 FLIF16ColorVal *v)
+{
+    RangesPrivFramecombine *data = src_ctx->priv_data;
+    if (p >= 3)
+        ff_static_snap(src_ctx, p, prev_planes, minv, maxv, v);
+    else
+        ff_flif16_ranges_snap(data->ranges, p, prev_planes, minv, maxv, v);
+}
+
+static void ff_framecombine_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivFramecombine *data = r_ctx->priv_data;
+    const FLIF16Ranges *range = flif16_ranges[data->ranges->r_no];
+    if (range->close) {
+        range->close(data->ranges);
+        av_freep(&data->ranges->priv_data);
+    }
+    av_freep(&data->ranges);
+}
+
+static const FLIF16Ranges flif16_ranges_static = {
+    .priv_data_size = sizeof(RangesPrivStatic),
+    .min            = &ff_static_min,
+    .max            = &ff_static_max,
+    .minmax         = &ff_static_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 1,
+    .close          = &ff_static_close
+};
+
+static const FLIF16Ranges flif16_ranges_channelcompact = {
+    .priv_data_size = sizeof(RangesPrivChannelcompact),
+    .min            = &ff_channelcompact_min,
+    .max            = &ff_channelcompact_max,
+    .minmax         = &ff_channelcompact_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 1,
+    .close          = NULL
+};
+
+static const FLIF16Ranges flif16_ranges_ycocg = {
+    .priv_data_size = sizeof(RangesPrivYCoCg),
+    .min            = &ff_ycocg_min,
+    .max            = &ff_ycocg_max,
+    .minmax         = &ff_ycocg_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 0,
+    .close          = &ff_ycocg_close
+};
+
+static const FLIF16Ranges flif16_ranges_permuteplanessubtract = {
+    .priv_data_size = sizeof(RangesPrivPermuteplanes),
+    .min            = &ff_permuteplanessubtract_min,
+    .max            = &ff_permuteplanessubtract_max,
+    .minmax         = &ff_permuteplanessubtract_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 0,
+    .close          = &ff_permuteplanes_close
+};
+
+static const FLIF16Ranges flif16_ranges_permuteplanes = {
+    .priv_data_size = sizeof(RangesPrivPermuteplanes),
+    .min            = &ff_permuteplanes_min,
+    .max            = &ff_permuteplanes_max,
+    .minmax         = &ff_static_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 0,
+    .close          = &ff_permuteplanes_close
+};
+
+static const FLIF16Ranges flif16_ranges_bounds = {
+    .priv_data_size = sizeof(RangesPrivBounds),
+    .min            = &ff_bounds_min,
+    .max            = &ff_bounds_max,
+    .minmax         = &ff_bounds_minmax,
+    .snap           = &ff_bounds_snap,
+    .is_static      = 0,
+    .close          = &ff_bounds_close
+};
+
+static const FLIF16Ranges flif16_ranges_palette = {
+    .priv_data_size = sizeof(RangesPrivPalette),
+    .min            = &ff_palette_min,
+    .max            = &ff_palette_max,
+    .minmax         = &ff_palette_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 0,
+    .close          = &ff_palette_close
+};
+
+static const FLIF16Ranges flif16_ranges_palettealpha = {
+    .priv_data_size = sizeof(RangesPrivPalette),
+    .min            = &ff_palettealpha_min,
+    .max            = &ff_palettealpha_max,
+    .minmax         = &ff_palettealpha_minmax,
+    .snap           = &ff_static_snap,
+    .is_static      = 0,
+    .close          = &ff_palette_close
+};
+
+static const FLIF16Ranges flif16_ranges_colorbuckets = {
+    .priv_data_size = sizeof(RangesPrivColorbuckets),
+    .min            = &ff_colorbuckets_min,
+    .max            = &ff_colorbuckets_max,
+    .minmax         = &ff_colorbuckets_minmax,
+    .snap           = &ff_colorbuckets_snap,
+    .is_static      = 0,
+    .close          = &ff_colorbuckets_close
+};
+
+static const FLIF16Ranges flif16_ranges_framecombine = {
+    .priv_data_size = sizeof(RangesPrivFramecombine),
+    .min            = &ff_framecombine_min,
+    .max            = &ff_framecombine_max,
+    .minmax         = &ff_framecombine_minmax,
+    .snap           = &ff_framecombine_snap,
+    .is_static      = 0,
+    .close          = &ff_framecombine_close
+};
+
+const FLIF16Ranges *flif16_ranges[] = {
+    [FLIF16_RANGES_PERMUTEPLANESSUBTRACT] = &flif16_ranges_permuteplanessubtract,
+    [FLIF16_RANGES_CHANNELCOMPACT]        = &flif16_ranges_channelcompact,
+    [FLIF16_RANGES_FRAMELOOKBACK]         = &flif16_ranges_framecombine,
+    [FLIF16_RANGES_PERMUTEPLANES]         = &flif16_ranges_permuteplanes,
+    [FLIF16_RANGES_COLORBUCKETS]          = &flif16_ranges_colorbuckets,
+    [FLIF16_RANGES_PALETTEALPHA]          = &flif16_ranges_palettealpha,
+    [FLIF16_RANGES_PALETTE]               = &flif16_ranges_palette,
+    [FLIF16_RANGES_BOUNDS]                = &flif16_ranges_bounds,
+    [FLIF16_RANGES_STATIC]                = &flif16_ranges_static,
+    [FLIF16_RANGES_YCOCG]                 = &flif16_ranges_ycocg
+};
+
+FLIF16RangesContext *ff_flif16_ranges_static_init(uint8_t num_planes,
+                                                  uint32_t bpc)
+{
+    const FLIF16Ranges *r = flif16_ranges[FLIF16_RANGES_STATIC];
+    FLIF16RangesContext *ctx;
+    RangesPrivStatic *data;
+    ctx = av_mallocz(sizeof(*ctx));
+    if (!ctx)
+        return NULL;
+    ctx->r_no       = FLIF16_RANGES_STATIC;
+    ctx->num_planes = num_planes;
+    ctx->priv_data  = av_mallocz(r->priv_data_size);
+    if (!ctx->priv_data) {
+        av_freep(&ctx);
+        return NULL;
+    }
+    data = ctx->priv_data;
+    data->bounds = av_malloc_array(num_planes, sizeof(*data->bounds));
+    if (!data->bounds) {
+        av_freep(&ctx);
+        av_freep(&ctx->priv_data);
+        return NULL;
+    }
+    for (unsigned int i = 0; i < num_planes; ++i) {
+        data->bounds[i][0] = 0;
+        data->bounds[i][1] = bpc;
+    }
+    return ctx;
+}
+
+void ff_flif16_ranges_close(FLIF16RangesContext* r_ctx) {
+    const FLIF16Ranges* ranges = flif16_ranges[r_ctx->r_no];
+    if (ranges->close)
+        ranges->close(r_ctx);
+    if (ranges->priv_data_size)
+        av_freep(&r_ctx->priv_data);
+    av_freep(&r_ctx);
+}
+
+static void ff_flif16_planes_get(FLIF16Context *ctx, FLIF16PixelData *frame,
+                                 FLIF16ColorVal *values, uint32_t row, uint32_t col)
+{
+    for (int i = 0; i < 3; i++)
+        values[i] = ff_flif16_pixel_get(ctx, frame, i, row, col);
+}
+
+static void ff_flif16_planes_set(FLIF16Context *ctx, FLIF16PixelData *frame,
+                                 FLIF16ColorVal *values, uint32_t row, uint32_t col)
+{
+    for (int i = 0; i < 3; i++)
+        ff_flif16_pixel_set(ctx, frame, i, row, col, values[i]);
+}
+
+/*
+ * =============================================================================
+ * Transforms
+ * =============================================================================
+ */
+
+/*
+ * YCoCg
+ */
+static int transform_ycocg_init(FLIF16TransformContext *ctx, FLIF16RangesContext *r_ctx)
+{
+    TransformPrivYCoCg *data = ctx->priv_data;
+
+    if (r_ctx->num_planes < 3                                            ||
+        ff_flif16_ranges_min(r_ctx, 0) == ff_flif16_ranges_max(r_ctx, 0) ||
+        ff_flif16_ranges_min(r_ctx, 1) == ff_flif16_ranges_max(r_ctx, 1) ||
+        ff_flif16_ranges_min(r_ctx, 2) == ff_flif16_ranges_max(r_ctx, 2) ||
+        ff_flif16_ranges_min(r_ctx, 0) < 0                               ||
+        ff_flif16_ranges_min(r_ctx, 1) < 0                               ||
+        ff_flif16_ranges_min(r_ctx, 2) < 0)
+        return 0;
+
+    data->origmax4 = FFMAX3(ff_flif16_ranges_max(r_ctx, 0),
+                            ff_flif16_ranges_max(r_ctx, 1),
+                            ff_flif16_ranges_max(r_ctx, 2))/4 + 1;
+    data->r_ctx = r_ctx;
+    return 1;
+}
+
+static FLIF16RangesContext *transform_ycocg_meta(FLIF16Context *ctx,
+                                                 FLIF16PixelData *frame,
+                                                 uint32_t frame_count,
+                                                 FLIF16TransformContext *t_ctx,
+                                                 FLIF16RangesContext *src_ctx)
+{
+    FLIF16RangesContext *r_ctx;
+    RangesPrivYCoCg *data;
+    TransformPrivYCoCg *trans_data = t_ctx->priv_data;
+    r_ctx = av_mallocz(sizeof(FLIF16RangesContext));
+    if (!r_ctx)
+        return NULL;
+    r_ctx->r_no = FLIF16_RANGES_YCOCG;
+    r_ctx->priv_data = av_mallocz(sizeof(RangesPrivYCoCg));
+    if (!r_ctx->priv_data)
+        return NULL;
+    data = r_ctx->priv_data;
+
+    data->origmax4 = trans_data->origmax4;
+    data->r_ctx    = trans_data->r_ctx;
+    r_ctx->num_planes = src_ctx->num_planes;
+    return r_ctx;
+}
+
+static void transform_ycocg_reverse(FLIF16Context *ctx,
+                                      FLIF16TransformContext *t_ctx,
+                                      FLIF16PixelData *pixel_data,
+                                      uint32_t stride_row,
+                                      uint32_t stride_col)
+{
+    int r, c;
+    FLIF16ColorVal RGB[3], YCOCG[3];
+    int height = ctx->height;
+    int width  = ctx->width;
+    TransformPrivYCoCg *data = t_ctx->priv_data;
+
+    for (r = 0; r<height; r+=stride_row) {
+        for (c = 0; c<width; c+=stride_col) {
+            ff_flif16_planes_get(ctx, pixel_data, YCOCG, r, c);
+
+            RGB[1] = YCOCG[0] - ((-YCOCG[2]) >> 1);
+            RGB[2] = YCOCG[0] + ((1 - YCOCG[2]) >> 1) - (YCOCG[1] >> 1);
+            RGB[0] = YCOCG[1] + RGB[2];
+
+            RGB[0] = av_clip(RGB[0], 0, ff_flif16_ranges_max(data->r_ctx, 0));
+            RGB[1] = av_clip(RGB[1], 0, ff_flif16_ranges_max(data->r_ctx, 1));
+            RGB[2] = av_clip(RGB[2], 0, ff_flif16_ranges_max(data->r_ctx, 2));
+
+            ff_flif16_planes_set(ctx, pixel_data, RGB, r, c);
+        }
+    }
+}
+
+/*
+ * PermutePlanes
+ */
+
+static int transform_permuteplanes_init(FLIF16TransformContext *ctx,
+                                           FLIF16RangesContext *r_ctx)
+{
+    TransformPrivPermuteplanes *data = ctx->priv_data;
+    ff_flif16_chancecontext_init(&data->ctx_a);
+
+    if (r_ctx->num_planes     < 3 ||
+        ff_flif16_ranges_min(r_ctx, 0) < 0 ||
+        ff_flif16_ranges_min(r_ctx, 1) < 0 ||
+        ff_flif16_ranges_min(r_ctx, 2) < 0)
+        return 0;
+
+    data->r_ctx = r_ctx;
+    return 1;
+}
+
+static int transform_permuteplanes_read(FLIF16TransformContext *ctx,
+                                           FLIF16Context *dec_ctx,
+                                           FLIF16RangesContext *r_ctx)
+{
+    int p;
+    TransformPrivPermuteplanes *data = ctx->priv_data;
+
+    switch (ctx->segment) {
+    case 0:
+        RAC_GET(&dec_ctx->rc, &data->ctx_a, 0, 1, &data->subtract,
+                FLIF16_RAC_NZ_INT);
+
+        for (p = 0; p<4; p++) {
+            data->from[p] = 0;
+            data->to[p] = 0;
+        }
+        ctx->segment = 1;
+
+    case 1:
+        for (; ctx->i < dec_ctx->num_planes; ++ctx->i) {
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, 0, dec_ctx->num_planes-1,
+                    &data->permutation[ctx->i],
+                    FLIF16_RAC_NZ_INT);
+            data->from[ctx->i] = 1;
+            data->to[ctx->i] = 1;
+        }
+        ctx->i = 0;
+
+        for (p = 0; p < dec_ctx->num_planes; p++) {
+            if (!data->from[p] || !data->to[p])
+            return 0;
+        }
+    }
+
+    ctx->segment = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_permuteplanes_meta(FLIF16Context *ctx,
+                                                         FLIF16PixelData *frame,
+                                                         uint32_t frame_count,
+                                                         FLIF16TransformContext *t_ctx,
+                                                         FLIF16RangesContext *src_ctx)
+{
+    int i;
+    FLIF16RangesContext *r_ctx;
+    TransformPrivPermuteplanes *data;
+    RangesPrivPermuteplanes *priv_data;
+
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    data = t_ctx->priv_data;
+    priv_data = av_mallocz(sizeof(*priv_data));
+    if (!priv_data)
+        return NULL;
+    if (data->subtract)
+        r_ctx->r_no = FLIF16_RANGES_PERMUTEPLANESSUBTRACT;
+    else
+        r_ctx->r_no = FLIF16_RANGES_PERMUTEPLANES;
+    r_ctx->num_planes = src_ctx->num_planes;
+    for (i = 0; i < 5; i++) {
+        priv_data->permutation[i] = data->permutation[i];
+    }
+    priv_data->r_ctx = data->r_ctx;
+    r_ctx->priv_data = priv_data;
+    return r_ctx;
+}
+
+static void transform_permuteplanes_reverse(FLIF16Context *ctx,
+                                              FLIF16TransformContext *t_ctx,
+                                              FLIF16PixelData *frame,
+                                              uint32_t stride_row,
+                                              uint32_t stride_col)
+{
+    int p, r, c;
+    FLIF16ColorVal pixel[5];
+    TransformPrivPermuteplanes *data = t_ctx->priv_data;
+    int height = ctx->height;
+    int width  = ctx->width;
+    FLIF16ColorVal val;
+
+    for (r = 0; r < height; r += stride_row) {
+        for (c = 0; c < width; c += stride_col) {
+            for (p = 0; p < data->r_ctx->num_planes; p++)
+                pixel[p] =  ff_flif16_pixel_get(ctx, frame, p, r, c);
+            for (p = 0; p < data->r_ctx->num_planes; p++)
+                ff_flif16_pixel_set(ctx, frame, data->permutation[p], r, c, pixel[p]);
+
+            ff_flif16_pixel_set(ctx, frame, data->permutation[0], r, c, pixel[0]);
+            if (!data->subtract) {
+                for (p = 1; p < data->r_ctx->num_planes; p++)
+                    ff_flif16_pixel_set(ctx, frame, data->permutation[p], r, c, pixel[p]);
+            } else {
+                for (p = 1; p < 3 && p < data->r_ctx->num_planes; p++) {
+                    val = av_clip(pixel[p] + pixel[0],
+                                  ff_flif16_ranges_min(data->r_ctx, data->permutation[p]),
+                                  ff_flif16_ranges_max(data->r_ctx, data->permutation[p]));
+                    ff_flif16_pixel_set(ctx, frame, data->permutation[p], r, c, val);
+                }
+                for (p = 3; p < data->r_ctx->num_planes; p++)
+                    ff_flif16_pixel_set(ctx, frame, data->permutation[p], r, c, pixel[p]);
+            }
+        }
+    }
+}
+
+/*
+ * ChannelCompact
+ */
+
+static int transform_channelcompact_init(FLIF16TransformContext *ctx,
+                                            FLIF16RangesContext *src_ctx)
+{
+    int p;
+    TransformPrivChannelcompact *data = ctx->priv_data;
+    if (src_ctx->num_planes > 4)
+        return 0;
+
+    for (p = 0; p < 4; p++) {
+        data->cpalette_inv_size[p] = 0;
+        data->cpalette_size[p]     = 0;
+        data->cpalette_inv[p]      = 0;
+        data->cpalette[p]          = 0;
+    }
+    ff_flif16_chancecontext_init(&data->ctx_a);
+    return 1;
+}
+
+static int transform_channelcompact_read(FLIF16TransformContext *ctx,
+                                         FLIF16Context *dec_ctx,
+                                         FLIF16RangesContext *src_ctx)
+{
+    unsigned int nb;
+    TransformPrivChannelcompact *data = ctx->priv_data;
+
+    for (; ctx->i < dec_ctx->num_planes; ctx->i++) {
+        switch (ctx->segment) {
+        case 0:
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, 0,
+                    ff_flif16_ranges_max(src_ctx, ctx->i) - ff_flif16_ranges_min(src_ctx, ctx->i),
+                    &nb, FLIF16_RAC_NZ_INT);
+            nb += 1;
+            data->min = ff_flif16_ranges_min(src_ctx, ctx->i);
+            data->cpalette[ctx->i] = av_malloc_array(nb, sizeof(FLIF16ColorVal));
+            if (!data->cpalette[ctx->i])
+                return AVERROR(ENOMEM);
+            data->cpalette_size[ctx->i] = nb;
+            data->remaining = nb-1;
+            ctx->segment = 1;
+
+        case 1:
+            for (; data->i < data->cpalette_size[ctx->i]; ++data->i) {
+                RAC_GET(&dec_ctx->rc, &data->ctx_a, 0,
+                        ff_flif16_ranges_max(src_ctx, ctx->i)- data->min - data->remaining,
+                        &data->cpalette[ctx->i][data->i],
+                        FLIF16_RAC_NZ_INT);
+                data->cpalette[ctx->i][data->i] += data->min;
+                data->min = data->cpalette[ctx->i][data->i]+1;
+                data->remaining--;
+            }
+            data->i = 0;
+            ctx->segment = 0;
+        }
+    }
+
+    ctx->i = 0;
+    ctx->segment = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_channelcompact_meta(FLIF16Context *ctx,
+                                                          FLIF16PixelData *frame,
+                                                          uint32_t frame_count,
+                                                          FLIF16TransformContext *t_ctx,
+                                                          FLIF16RangesContext *src_ctx)
+{
+    int i;
+    FLIF16RangesContext *r_ctx;
+    RangesPrivChannelcompact *data;
+    TransformPrivChannelcompact *trans_data;
+
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    data = av_mallocz(sizeof(*data));
+    if (!data) {
+        av_freep(&r_ctx);
+        return NULL;
+    }
+    trans_data = t_ctx->priv_data;
+    r_ctx->num_planes = src_ctx->num_planes;
+    for (i = 0; i < src_ctx->num_planes; i++) {
+        data->nb_colors[i] = trans_data->cpalette_size[i] - 1;
+    }
+    r_ctx->priv_data = data;
+    r_ctx->r_no = FLIF16_RANGES_CHANNELCOMPACT;
+    ff_static_close(src_ctx);
+    av_freep(&src_ctx->priv_data);
+    av_freep(&src_ctx);
+
+    return r_ctx;
+}
+
+static void transform_channelcompact_reverse(FLIF16Context *ctx,
+                                            FLIF16TransformContext *t_ctx,
+                                            FLIF16PixelData *frame,
+                                            uint32_t stride_row,
+                                            uint32_t stride_col)
+{
+    int p, P;
+    uint32_t r, c;
+    FLIF16ColorVal *palette;
+    size_t palette_size;
+    TransformPrivChannelcompact *data = t_ctx->priv_data;
+
+    for (p = 0; p < ctx->num_planes; p++) {
+        palette_size = data->cpalette_size[p];
+        palette      = data->cpalette[p];
+
+        for (r = 0; r < ctx->height; r += stride_row) {
+            for (c = 0; c < ctx->width; c += stride_col) {
+                P = ff_flif16_pixel_get(ctx, frame, p, r, c);
+                if (P < 0 || P >= (int) palette_size)
+                    P = 0;
+                av_assert1(P < (int) palette_size);
+                ff_flif16_pixel_set(ctx, frame, p, r, c, palette[P]);
+            }
+        }
+    }
+}
+
+static void transform_channelcompact_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivChannelcompact *data = ctx->priv_data;
+    for (unsigned int i = 0; i < 4; i++) {
+        av_freep(&data->cpalette[i]);
+
+        if (data->cpalette_inv_size[i])
+            av_freep(&data->cpalette_inv[i]);
+    }
+}
+
+/*
+ * Bounds
+ */
+
+static int transform_bounds_init(FLIF16TransformContext *ctx,
+                                 FLIF16RangesContext *src_ctx)
+{
+    TransformPrivBounds *data = ctx->priv_data;
+    if (src_ctx->num_planes > 4)
+        return 0;
+    ff_flif16_chancecontext_init(&data->ctx_a);
+    data->bounds = av_malloc_array(src_ctx->num_planes, sizeof(*data->bounds));
+    if (!data->bounds)
+        return AVERROR(ENOMEM);
+    return 1;
+}
+
+static int transform_bounds_read(FLIF16TransformContext *ctx,
+                                 FLIF16Context *dec_ctx,
+                                 FLIF16RangesContext *src_ctx)
+{
+    TransformPrivBounds *data = ctx->priv_data;
+    int max;
+
+    for (; ctx->i < dec_ctx->num_planes; ctx->i++) {
+        switch (ctx->segment) {
+        case 0:
+            ff_flif16_ranges_min(src_ctx, ctx->i);
+            ff_flif16_ranges_max(src_ctx, ctx->i);
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, ff_flif16_ranges_min(src_ctx, ctx->i),
+                    ff_flif16_ranges_max(src_ctx, ctx->i), &data->min, FLIF16_RAC_GNZ_INT);
+            ctx->segment = 1;
+
+        case 1:
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, data->min,
+                    ff_flif16_ranges_max(src_ctx, ctx->i), &max, FLIF16_RAC_GNZ_INT);
+            if (data->min > max)
+                return 0;
+            if (data->min < ff_flif16_ranges_min(src_ctx, ctx->i))
+                return 0;
+            if (max > ff_flif16_ranges_max(src_ctx, ctx->i))
+                return 0;
+            data->bounds[ctx->i][0] = data->min;
+            data->bounds[ctx->i][1] = max;
+            ctx->segment = 0;
+        }
+    }
+
+    ctx->i = 0;
+    ctx->segment = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_bounds_meta(FLIF16Context *ctx,
+                                                  FLIF16PixelData *frame,
+                                                  uint32_t frame_count,
+                                                  FLIF16TransformContext *t_ctx,
+                                                  FLIF16RangesContext *src_ctx)
+{
+    FLIF16RangesContext *r_ctx;
+    TransformPrivBounds *trans_data = t_ctx->priv_data;
+    RangesPrivStatic *data;
+    RangesPrivBounds *dataB;
+
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    r_ctx->num_planes = src_ctx->num_planes;
+
+    if (flif16_ranges[src_ctx->r_no]->is_static) {
+        r_ctx->r_no = FLIF16_RANGES_STATIC;
+        r_ctx->priv_data = av_mallocz(sizeof(*data));
+        if (!r_ctx->priv_data) {
+            av_freep(&r_ctx);
+            return NULL;
+        }
+        av_freep(&src_ctx);
+        data = r_ctx->priv_data;
+        data->bounds = trans_data->bounds;
+    } else {
+        r_ctx->r_no = FLIF16_RANGES_BOUNDS;
+        r_ctx->priv_data = av_mallocz(sizeof(*dataB));
+        if (!r_ctx->priv_data) {
+            av_freep(&r_ctx);
+            return NULL;
+        }
+        dataB = r_ctx->priv_data;
+        dataB->bounds = trans_data->bounds;
+        dataB->r_ctx = src_ctx;
+    }
+
+    return r_ctx;
+}
+
+/*
+ * Palette
+ */
+
+#define MAX_PALETTE_SIZE 30000
+
+static int transform_palette_init(FLIF16TransformContext *ctx,
+                                     FLIF16RangesContext *src_ctx)
+{
+    TransformPrivPalette *data = ctx->priv_data;
+
+    if ((src_ctx->num_planes < 3)
+              ||
+        (ff_flif16_ranges_max(src_ctx, 0) == 0 &&
+         ff_flif16_ranges_max(src_ctx, 2) == 0 &&
+         src_ctx->num_planes > 3               &&
+         ff_flif16_ranges_min(src_ctx, 3) == 1 &&
+         ff_flif16_ranges_max(src_ctx, 3) == 1)
+              ||
+        (ff_flif16_ranges_min(src_ctx, 1) == ff_flif16_ranges_max(src_ctx, 1) &&
+         ff_flif16_ranges_min(src_ctx, 2) == ff_flif16_ranges_max(src_ctx, 2)))
+        return 0;
+
+    if (src_ctx->num_planes > 3)
+        data->has_alpha = 1;
+    else
+        data->has_alpha = 0;
+
+    ff_flif16_chancecontext_init(&data->ctx);
+    ff_flif16_chancecontext_init(&data->ctxY);
+    ff_flif16_chancecontext_init(&data->ctxI);
+    ff_flif16_chancecontext_init(&data->ctxQ);
+    data->p = 0;
+
+    return 1;
+}
+
+static int transform_palette_read(FLIF16TransformContext *ctx,
+                                     FLIF16Context *dec_ctx,
+                                     FLIF16RangesContext *src_ctx)
+{
+    TransformPrivPalette *data = ctx->priv_data;
+
+    switch (ctx->i) {
+    case 0:
+        RAC_GET(&dec_ctx->rc, &data->ctx, 1, MAX_PALETTE_SIZE,
+                &data->size, FLIF16_RAC_GNZ_INT);
+        data->palette = av_malloc_array(data->size, sizeof(*data->palette));
+        if (!data->palette)
+            return AVERROR(ENOMEM);
+        ctx->i = 1;
+
+    case 1:
+        RAC_GET(&dec_ctx->rc, &data->ctx, 0, 1,
+                &data->sorted, FLIF16_RAC_GNZ_INT);
+        if (data->sorted) {
+            ctx->i = 2;
+            for (int i = 0; i < 3; i++) {
+                data->min[i] = ff_flif16_ranges_min(src_ctx, i);
+                data->max[i] = ff_flif16_ranges_max(src_ctx, i);
+                data->palette[0][i] = -1;
+            }
+            data->prev = data->palette[0];
+        } else {
+            ctx->i = 5;
+        }
+    }
+
+    for (; data->p < data->size; data->p++) {
+        switch (ctx->i) {
+        case 2:
+            RAC_GET(&dec_ctx->rc, &data->ctxY, data->min[0], data->max[0],
+                    &data->Y, FLIF16_RAC_GNZ_INT);
+            data->pp[0] = data->Y;
+            ff_flif16_ranges_minmax(src_ctx, 1, data->pp, &data->min[1], &data->max[1]);
+            ctx->i = 3;
+
+        case 3:
+            RAC_GET(&dec_ctx->rc, &data->ctxI,
+                    data->prev[0] == data->Y ? data->prev[1] : data->min[1],
+                    data->max[1],
+                    &data->I, FLIF16_RAC_GNZ_INT);
+            data->pp[1] = data->I;
+            ff_flif16_ranges_minmax(src_ctx, 2, data->pp, &data->min[2], &data->max[2]);
+            ctx->i = 4;
+
+        case 4:
+            RAC_GET(&dec_ctx->rc, &data->ctxQ, data->min[2], data->max[2],
+                    &data->Q, FLIF16_RAC_GNZ_INT);
+            data->palette[data->p][0] = data->Y;
+            data->palette[data->p][1] = data->I;
+            data->palette[data->p][2] = data->Q;
+            data->min[0] = data->Y;
+            data->prev = data->palette[data->p];
+            ctx->i = 2;
+        }
+    }
+
+    for (; data->p < data->size; data->p++) {
+        switch (ctx->i) {
+        case 5:
+            ff_flif16_ranges_minmax(src_ctx, 0, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxY, data->min[0], data->max[0],
+                    &data->Y, FLIF16_RAC_GNZ_INT);
+            data->pp[0] = data->Y;
+            ctx->i = 6;
+
+        case 6:
+            ff_flif16_ranges_minmax(src_ctx, 1, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxI, data->min[0], data->max[0],
+                    &data->I, FLIF16_RAC_GNZ_INT);
+            data->pp[1] = data->I;
+            ctx->i = 7;
+
+        case 7:
+            ff_flif16_ranges_minmax(src_ctx, 2, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxQ, data->min[0], data->max[0],
+                    &data->Q, FLIF16_RAC_GNZ_INT);
+            data->palette[data->p][0] = data->Y;
+            data->palette[data->p][1] = data->I;
+            data->palette[data->p][2] = data->Q;
+            ctx->i = 5;
+        }
+    }
+
+    ctx->i = 0;
+    data->p = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_palette_meta(FLIF16Context *ctx,
+                                                   FLIF16PixelData *frame,
+                                                   uint32_t frame_count,
+                                                   FLIF16TransformContext *t_ctx,
+                                                   FLIF16RangesContext *src_ctx)
+{
+    FLIF16RangesContext *r_ctx;
+    TransformPrivPalette *trans_data;
+    RangesPrivPalette *data;
+
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    trans_data = t_ctx->priv_data;
+    data = av_mallocz(sizeof(*data));
+
+    if (!data) {
+        av_freep(&r_ctx);
+        return NULL;
+    }
+
+    data->r_ctx = src_ctx;
+    data->nb_colors = trans_data->size;
+    r_ctx->r_no = FLIF16_RANGES_PALETTE;
+    r_ctx->num_planes = src_ctx->num_planes;
+    r_ctx->priv_data = data;
+    return r_ctx;
+}
+
+static void transform_palette_reverse(FLIF16Context *ctx,
+                                     FLIF16TransformContext *t_ctx,
+                                     FLIF16PixelData *frame,
+                                     uint32_t stride_row,
+                                     uint32_t stride_col)
+{
+    int r, c;
+    int P;
+    FLIF16ColorVal (*v)[3];
+    TransformPrivPalette *data = t_ctx->priv_data;
+    for (r = 0; r < ctx->height; r += stride_row) {
+        for (c = 0; c < ctx->width; c += stride_col) {
+            P = ff_flif16_pixel_get(ctx, frame, 1, r, c);
+            if (P < 0 || P >= data->size)
+                P = 0;
+            av_assert1(P < data->size);
+            av_assert1(P >= 0);
+
+            v = &data->palette[P];
+            for (unsigned int i = 0; i < 3; i++)
+                ff_flif16_pixel_set(ctx, frame, i, r, c, (*v)[i]);
+        }
+    }
+}
+
+static void transform_palette_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivPalette *data = ctx->priv_data;
+    av_freep(&data->palette);
+}
+
+/*
+ * Palette Alpha
+ */
+
+static int transform_palettealpha_init(FLIF16TransformContext *ctx,
+                                          FLIF16RangesContext *src_ctx)
+{
+    TransformPrivPalettealpha *data = ctx->priv_data;
+    if (src_ctx->num_planes < 4 ||
+        ff_flif16_ranges_min(src_ctx, 3) == ff_flif16_ranges_max(src_ctx, 3))
+        return 0;
+
+    data->already_has_palette = 0;
+    ff_flif16_chancecontext_init(&data->ctx);
+    ff_flif16_chancecontext_init(&data->ctxY);
+    ff_flif16_chancecontext_init(&data->ctxI);
+    ff_flif16_chancecontext_init(&data->ctxQ);
+    ff_flif16_chancecontext_init(&data->ctxA);
+    data->p = 0;
+
+    return 1;
+}
+
+static int transform_palettealpha_read(FLIF16TransformContext *ctx,
+                                          FLIF16Context *dec_ctx,
+                                          FLIF16RangesContext *src_ctx)
+{
+    TransformPrivPalettealpha *data = ctx->priv_data;
+
+    switch (ctx->i) {
+    case 0:
+        RAC_GET(&dec_ctx->rc, &data->ctx, 1, MAX_PALETTE_SIZE,
+                &data->size, FLIF16_RAC_GNZ_INT);
+        data->palette = av_malloc_array(data->size, sizeof(*data->palette));
+        if (!data->palette)
+            return 0;
+        ctx->i++;
+
+    case 1:
+        RAC_GET(&dec_ctx->rc, &data->ctx, 0, 1,
+                &data->sorted, FLIF16_RAC_GNZ_INT);
+        if (data->sorted) {
+            ctx->i = 2;
+            data->min[0] = ff_flif16_ranges_min(src_ctx, 3);
+            data->max[0] = ff_flif16_ranges_max(src_ctx, 3);
+            for (int i = 1; i < 4; i++) {
+                data->min[i] = ff_flif16_ranges_min(src_ctx, i-1);
+                data->max[i] = ff_flif16_ranges_max(src_ctx, i-1);
+                data->palette[0][i] = -1;
+            }
+            data->prev = data->palette[0];
+        } else {
+            ctx->i = 6;
+        }
+    }
+
+    for (; data->p < data->size && ctx->i < 6; data->p++) {
+        switch (ctx->i) {
+        case 2:
+            RAC_GET(&dec_ctx->rc, &data->ctxA, data->min[0], data->max[0],
+                    &data->A, FLIF16_RAC_GNZ_INT);
+            if (data->alpha_zero_special && data->A == 0) {
+                for (int i = 0; i < 4; i++)
+                    data->palette[data->p][i] = 0;
+                break;
+            }
+            ctx->i = 3;
+
+        case 3:
+            RAC_GET(&dec_ctx->rc, &data->ctxY,
+                    data->prev[0] == data->A ? data->prev[1] : data->min[1],
+                    data->max[1],
+                    &data->Y, FLIF16_RAC_GNZ_INT);
+            data->pp[0] = data->Y;
+            ff_flif16_ranges_minmax(src_ctx, 1, data->pp, &data->min[2], &data->max[2]);
+            ctx->i = 4;
+
+        case 4:
+            RAC_GET(&dec_ctx->rc, &data->ctxI,
+                    data->min[2], data->max[2],
+                    &data->I, FLIF16_RAC_GNZ_INT);
+            data->pp[1] = data->I;
+            ff_flif16_ranges_minmax(src_ctx, 2, data->pp, &data->min[3], &data->max[3]);
+            ctx->i = 5;
+
+        case 5:
+            RAC_GET(&dec_ctx->rc, &data->ctxQ, data->min[3], data->max[3],
+                    &data->Q, FLIF16_RAC_GNZ_INT);
+            data->palette[data->p][0] = data->A;
+            data->palette[data->p][1] = data->Y;
+            data->palette[data->p][2] = data->I;
+            data->palette[data->p][3] = data->Q;
+            data->min[0] = data->A;
+            data->prev = data->palette[data->p];
+            ctx->i = 2;
+        }
+    }
+
+    for (; data->p < data->size && ctx->i >=6; data->p++) {
+        switch (ctx->i) {
+        case 6:
+            RAC_GET(&dec_ctx->rc, &data->ctxA,
+            ff_flif16_ranges_min(src_ctx, 3), ff_flif16_ranges_max(src_ctx, 3),
+            &data->A, FLIF16_RAC_GNZ_INT);
+            if (data->alpha_zero_special && data->A == 0) {
+                for (int i = 0; i < 4; i++)
+                    data->palette[data->p][i] = 0;
+                data->p++;
+            }
+            ctx->i = 7;
+
+        case 7:
+            ff_flif16_ranges_minmax(src_ctx, 0, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxY, data->min[0], data->max[0],
+                    &data->Y, FLIF16_RAC_GNZ_INT);
+            data->pp[0] = data->Y;
+            ctx->i = 8;
+
+        case 8:
+            ff_flif16_ranges_minmax(src_ctx, 1, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxI, data->min[0], data->max[0],
+                    &data->I, FLIF16_RAC_GNZ_INT);
+            data->pp[1] = data->I;
+            ctx->i = 9;
+
+        case 9:
+            ff_flif16_ranges_minmax(src_ctx, 2, data->pp, &data->min[0], &data->max[0]);
+            RAC_GET(&dec_ctx->rc, &data->ctxQ, data->min[0], data->max[0],
+                    &data->Q, FLIF16_RAC_GNZ_INT);
+            data->palette[data->p][0] = data->A;
+            data->palette[data->p][1] = data->Y;
+            data->palette[data->p][2] = data->I;
+            data->palette[data->p][3] = data->Q;
+            ctx->i = 6;
+        }
+    }
+
+    data->p = 0;
+    ctx->i = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static void transform_palettealpha_configure(FLIF16TransformContext *ctx,
+                                             const int setting)
+{
+    TransformPrivPalettealpha *data = ctx->priv_data;
+    data->alpha_zero_special = setting;
+    if (setting > 0) {
+        data->ordered_palette = 1;
+        data->max_palette_size = setting;
+    } else {
+        data->ordered_palette = 0;
+        data->max_palette_size = -setting;
+    }
+}
+
+static FLIF16RangesContext *transform_palettealpha_meta(FLIF16Context *ctx,
+                                                        FLIF16PixelData *frame,
+                                                        uint32_t frame_count,
+                                                        FLIF16TransformContext *t_ctx,
+                                                        FLIF16RangesContext *src_ctx)
+{
+    FLIF16RangesContext *r_ctx;
+    TransformPrivPalettealpha *data;
+    RangesPrivPalette *priv_data;
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    data = t_ctx->priv_data;
+
+    priv_data = av_mallocz(sizeof(*priv_data));
+    if (!priv_data) {
+        av_freep(&r_ctx);
+        return NULL;
+    }
+    r_ctx->r_no = FLIF16_RANGES_PALETTEALPHA;
+    r_ctx->num_planes = src_ctx->num_planes;
+    priv_data->nb_colors = data->size;
+    priv_data->r_ctx = src_ctx;
+    r_ctx->priv_data = priv_data;
+
+    return r_ctx;
+}
+
+static void transform_palettealpha_reverse(FLIF16Context *ctx,
+                                          FLIF16TransformContext *t_ctx,
+                                          FLIF16PixelData *frame,
+                                          uint32_t stride_row,
+                                          uint32_t stride_col)
+{
+    int r, c;
+    int P;
+    TransformPrivPalettealpha *data = t_ctx->priv_data;
+    for (r = 0; r < ctx->height; r += stride_row) {
+        for (c = 0; c < ctx->width; c += stride_col) {
+            P = ff_flif16_pixel_get(ctx, frame, 1, r, c);
+            av_assert1(P < data->size);
+            ff_flif16_pixel_set(ctx, frame, 0, r, c, data->palette[P][1]);
+            ff_flif16_pixel_set(ctx, frame, 1, r, c, data->palette[P][2]);
+            ff_flif16_pixel_set(ctx, frame, 2, r, c, data->palette[P][3]);
+            ff_flif16_pixel_set(ctx, frame, 3, r, c, data->palette[P][0]);
+        }
+    }
+}
+
+static void transform_palettealpha_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivPalettealpha *data = ctx->priv_data;
+    av_freep(&data->palette);
+}
+
+/*
+ * ColorBuckets
+ */
+
+static int ff_remove_color(ColorBucket *cb, const FLIF16ColorVal c)
+{
+    if (cb->discrete) {
+        unsigned int pos = 0;
+        ColorValCB_list *temp = cb->values;
+        ColorValCB_list *prev = 0;
+        for (; pos < cb->values_size; pos++, temp = temp->next) {
+            if (c == temp->data) {
+                if (prev && temp != cb->values_last) {
+                    prev->next = temp->next;
+                    av_freep(&temp);
+                } else if (temp == cb->values_last) {
+                    cb->values_last = prev;
+                    cb->values_last->next = NULL;
+                    av_freep(&temp);
+                } else if (!prev) {
+                    cb->values = temp->next;
+                    av_freep(&temp);
+                }
+                cb->values_size--;
+                break;
+            }
+            prev = temp;
+        }
+        if (cb->values_size == 0) {
+            cb->min = 10000;
+            cb->max = -10000;
+            return 1;
+        }
+        av_assert1(cb->values_size > 0);
+        if (c == cb->min)
+            cb->min = cb->values->data;
+        if (c == cb->max)
+            cb->max = cb->values_last->data;
+    } else {
+        if (c == cb->min)
+            cb->min++;
+        if (c == cb->max)
+            cb->max--;
+        if (c > cb->max)
+            return 1;
+        if (c < cb->min)
+            return 1;
+        cb->discrete = 1;
+        av_freep(&cb->values);
+        cb->values_size = 0;
+        for (FLIF16ColorVal x = cb->min; x <= cb->max; x++) {
+            if (x != c) {
+                if (cb->values_size == 0) {
+                    cb->values = av_mallocz(sizeof(*cb->values));
+                    if (!cb->values)
+                        return AVERROR(ENOMEM);
+                    cb->values_last = cb->values;
+                } else {
+                    cb->values_last->next = av_mallocz(sizeof(*cb->values_last->next));
+                    if (!cb->values_last->next)
+                        return AVERROR(ENOMEM);
+                    cb->values_last = cb->values_last->next;
+                }
+                cb->values_last->data = x;
+                cb->values_size++;
+            }
+        }
+        cb->values_last->next = NULL;
+    }
+    return 1;
+}
+
+static FLIF16ColorVal ff_snap_color_slow(ColorBucket *cb, const FLIF16ColorVal c)
+{
+    FLIF16ColorVal diff;
+    FLIF16ColorVal d;
+    if (c <= cb->min)
+        return cb->min;
+    if (c >= cb->max)
+        return cb->max;
+    if (cb->discrete) {
+        FLIF16ColorVal mindiff = abs(c - cb->min);
+        ColorValCB_list *best = cb->values;
+        ColorValCB_list *temp = cb->values->next;
+        for (unsigned int i = 1; i < cb->values_size; i++, temp = temp->next) {
+            if (c == temp->data)
+                return c;
+            diff = abs(c - temp->data);
+            if (diff < mindiff) {
+                best = temp;
+                mindiff = diff;
+            }
+            if (temp->data > c)
+                break;
+        }
+        d = best->data;
+        return d;
+    }
+    return c;
+}
+
+static void ff_prepare_snapvalues(ColorBucket *cb)
+{
+    int i = 0;
+    if (cb->discrete) {
+        if (cb->max > cb->min) {
+            cb->snapvalues = av_malloc_array((cb->max - cb->min), sizeof(*cb->snapvalues));
+            cb->snapvalues_size = cb->max - cb->min;
+        }
+        if (cb->max - cb->min > 0)
+            av_assert0(cb->snapvalues != NULL);
+        for (FLIF16ColorVal c = cb->min; c < cb->max; c++) {
+            cb->snapvalues[i] = ff_snap_color_slow(cb, c);
+            i++;
+        }
+    }
+}
+
+static uint8_t ff_colorbuckets_exists2(ColorBuckets *cb, const int p,
+                                       FLIF16ColorVal *pp)
+{
+    FLIF16ColorVal rmin, rmax, v;
+    ColorBucket *b;
+    if (p > FLIF16_PLANE_Y &&
+       (pp[0] < cb->min0 || pp[0] > ff_flif16_ranges_max(cb->ranges, 0))) {
+        return 0;
+    }
+    if (p > FLIF16_PLANE_CO &&
+       (pp[1] < cb->min1 || pp[1] > ff_flif16_ranges_max(cb->ranges, 1))) {
+        return 0;
+    }
+
+    v = pp[p];
+    ff_flif16_ranges_snap(cb->ranges, p, pp, &rmin, &rmax, &v);
+    if (v != pp[p])
+        return 0;
+
+    b = ff_bucket_buckets(cb, p, pp);
+    if (ff_snap_color_slow(b, pp[p]) != pp[p])
+        return 0;
+
+    return 1;
+}
+
+static uint8_t ff_colorbuckets_exists(ColorBuckets *cb, const int p,
+                                      FLIF16ColorVal *lower, FLIF16ColorVal *upper)
+{
+    FLIF16ColorVal pixel[2];
+    pixel[0] = lower[0];
+    pixel[1] = lower[1];
+    if (p == FLIF16_PLANE_Y) {
+        for (pixel[0] = lower[0]; pixel[0] <= upper[0]; pixel[0]++) {
+            if (ff_colorbuckets_exists2(cb, p, pixel))
+                return 1;
+        }
+    }
+    if (p == FLIF16_PLANE_CO) {
+        for (pixel[0] = lower[0]; pixel[0] <= upper[0]; pixel[0]++) {
+            for (pixel[1] = lower[1]; pixel[1] <= upper[1]; pixel[1]++) {
+                if (ff_colorbuckets_exists2(cb, p, pixel))
+                    return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+static int transform_colorbuckets_init(FLIF16TransformContext *ctx,
+                                          FLIF16RangesContext *src_ctx)
+{
+    TransformPrivColorbuckets *data = ctx->priv_data;
+    int length, temp;
+    ColorBuckets *cb;
+    data->cb = NULL;
+    data->really_used = 0;
+    if ((src_ctx->num_planes < 3)
+            ||
+       (ff_flif16_ranges_min(src_ctx, 0) == 0 &&
+        ff_flif16_ranges_max(src_ctx, 0) == 0 &&
+        ff_flif16_ranges_min(src_ctx, 2) == 0 &&
+        ff_flif16_ranges_max(src_ctx, 2) == 0)
+            ||
+       (ff_flif16_ranges_min(src_ctx, 0) == ff_flif16_ranges_max(src_ctx, 0) &&
+        ff_flif16_ranges_min(src_ctx, 1) == ff_flif16_ranges_max(src_ctx, 1) &&
+        ff_flif16_ranges_min(src_ctx, 2) == ff_flif16_ranges_max(src_ctx, 2))
+            ||
+       (ff_flif16_ranges_max(src_ctx, 0) - ff_flif16_ranges_min(src_ctx, 0) > 1023 ||
+        ff_flif16_ranges_max(src_ctx, 1) - ff_flif16_ranges_min(src_ctx, 1) > 1023 ||
+        ff_flif16_ranges_max(src_ctx, 2) - ff_flif16_ranges_min(src_ctx, 2) > 1023)
+            ||
+       (ff_flif16_ranges_min(src_ctx, 1) == ff_flif16_ranges_max(src_ctx, 1)))
+        return 0;
+
+    cb = av_mallocz(sizeof(*cb));
+    if (!cb)
+        return AVERROR(ENOMEM);
+
+    ff_init_bucket_default(&cb->bucket0);
+    cb->min0 = ff_flif16_ranges_min(src_ctx, 0);
+    cb->min1 = ff_flif16_ranges_min(src_ctx, 1);
+
+    length = ((ff_flif16_ranges_max(src_ctx, 0) - cb->min0)/1 + 1);
+    temp = ((ff_flif16_ranges_max(src_ctx, 1) - cb->min1)/4 + 1);
+
+    cb->bucket1 = av_malloc_array(((ff_flif16_ranges_max(src_ctx, 0) - cb->min0)/1 + 1),
+                                    sizeof(*cb->bucket1));
+    if (!cb->bucket1) {
+        av_freep(&cb);
+        return AVERROR(ENOMEM);
+    }
+    cb->bucket1_size = ((ff_flif16_ranges_max(src_ctx, 0)
+                                   - cb->min0)/1 + 1);
+    for (unsigned int i = 0; i < cb->bucket1_size; i++)
+        ff_init_bucket_default(&cb->bucket1[i]);
+    cb->bucket2 = av_malloc_array(length, sizeof(*cb->bucket2));
+    if (!cb->bucket2) {
+        av_freep(&cb->bucket1);
+        av_freep(&cb);
+        return AVERROR(ENOMEM);
+    }
+    cb->bucket2_size = length;
+    for (unsigned int i = 0; i < length; i++) {
+        cb->bucket2_list_size = temp;
+        cb->bucket2[i] = av_malloc_array(temp, sizeof(*cb->bucket2[i]));
+
+        if (!cb->bucket2[i]) {
+            av_freep(&cb->bucket1);
+            av_freep(&cb->bucket2);
+            av_freep(&cb);
+            return AVERROR(ENOMEM);
+        }
+
+        for (unsigned int j = 0; j < temp; j++)
+            ff_init_bucket_default(&cb->bucket2[i][j]);
+    }
+    ff_init_bucket_default(&cb->bucket3);
+    for (uint8_t i = 0; i < 6; i++)
+        ff_flif16_chancecontext_init(&data->ctx[i]);
+
+    cb->ranges = src_ctx;
+    data->cb = cb;
+    data->i = 0;
+
+    return 1;
+}
+
+static FLIF16RangesContext *transform_colorbuckets_meta(FLIF16Context *ctx,
+                                                        FLIF16PixelData *frame,
+                                                        uint32_t frame_count,
+                                                        FLIF16TransformContext *t_ctx,
+                                                        FLIF16RangesContext *src_ctx)
+{
+    FLIF16RangesContext *r_ctx;
+    TransformPrivColorbuckets *trans_data = t_ctx->priv_data;
+    RangesPrivColorbuckets *data;
+    ColorBuckets *cb = trans_data->cb;
+    FLIF16ColorVal pixelL[2], pixelU[2];
+
+    r_ctx = av_mallocz(sizeof(*r_ctx));
+    if (!r_ctx)
+        return NULL;
+    data = av_mallocz(sizeof(*data));
+    if (!data) {
+        av_freep(&r_ctx);
+        return NULL;
+    }
+    if (ff_flif16_ranges_min(src_ctx, 2) < ff_flif16_ranges_max(src_ctx, 2)) {
+        pixelL[0] = cb->min0;
+        pixelU[0] = cb->min0 + 1 - 1;
+        pixelL[1] = cb->min1;
+        pixelU[1] = cb->min1 + 4 - 1;
+        for (int i = 0; i < cb->bucket2_size; i++) {
+            pixelL[1] = cb->min1;
+            pixelU[1] = cb->min1 + 4 - 1;
+            for (int j = 0; j < cb->bucket2_list_size; j++) {
+                if (cb->bucket2[i][j].min > cb->bucket2[i][j].max) {
+                    for (FLIF16ColorVal c = pixelL[1]; c <= pixelU[1]; c++) {
+                        if (!ff_remove_color(ff_bucket_buckets2(cb, 1, pixelL), c))
+                            return NULL;
+                        if (!ff_remove_color(ff_bucket_buckets2(cb, 1, pixelU), c))
+                            return NULL;
+                    }
+                }
+                pixelL[1] += 4;
+                pixelU[1] += 4;
+            }
+            pixelL[0] += 1;
+            pixelU[0] += 1;
+        }
+    }
+    ff_prepare_snapvalues(&cb->bucket0);
+    ff_prepare_snapvalues(&cb->bucket3);
+    for (unsigned int i = 0; i < cb->bucket1_size; i++)
+        ff_prepare_snapvalues(&cb->bucket1[i]);
+    for (unsigned int i = 0; i < cb->bucket2_size; i++) {
+        for (unsigned int j = 0; j < cb->bucket2_list_size; j++)
+            ff_prepare_snapvalues(&cb->bucket2[i][j]);
+    }
+
+    trans_data->really_used = 1;
+
+    data->r_ctx = src_ctx;
+    data->buckets = trans_data->cb;
+
+    r_ctx->r_no = FLIF16_RANGES_COLORBUCKETS;
+    r_ctx->priv_data = data;
+    r_ctx->num_planes = src_ctx->num_planes;
+
+    return r_ctx;
+}
+
+static void transform_colorbuckets_minmax(FLIF16RangesContext *src_ctx, int p,
+                                          FLIF16ColorVal *lower,
+                                          FLIF16ColorVal *upper,
+                                          FLIF16ColorVal *smin,
+                                          FLIF16ColorVal *smax)
+{
+    FLIF16ColorVal rmin, rmax;
+    FLIF16ColorVal pixel[2];
+    pixel[0] = lower[0];
+    pixel[1] = lower[1];
+    *smin = 10000;
+    *smax = -10000;
+    if (p == FLIF16_PLANE_Y) {
+        ff_flif16_ranges_minmax(src_ctx, p,pixel,smin,smax);
+    }
+    else if (p == FLIF16_PLANE_CO) {
+        for (pixel[0] = lower[0]; pixel[0] <= upper[0]; pixel[0]++) {
+            ff_flif16_ranges_minmax(src_ctx, p, pixel, &rmin, &rmax);
+            *smin = FFMIN(*smin, rmin);
+            *smax = FFMAX(*smax, rmax);
+        }
+    }
+    else if (p == FLIF16_PLANE_CG) {
+        for (pixel[0] = lower[0]; pixel[0] <= upper[0]; pixel[0]++) {
+            for (pixel[1] = lower[1]; pixel[1] <= upper[1]; pixel[1]++) {
+                ff_flif16_ranges_minmax(src_ctx, p, pixel, &rmin, &rmax);
+                *smin = FFMIN(*smin, rmin);
+                *smax = FFMAX(*smax, rmax);
+            }
+        }
+    }
+    else if (p == FLIF16_PLANE_ALPHA) {
+        ff_flif16_ranges_minmax(src_ctx, p, pixel, smin, smax);
+    }
+}
+
+static const unsigned int max_per_colorbucket[] = {255, 510, 5, 255};
+
+static int ff_load_bucket(FLIF16RangeCoder *rc, FLIF16ChanceContext *chancectx,
+                          ColorBucket *b, ColorBuckets *cb,
+                          FLIF16RangesContext *src_ctx, int plane,
+                          FLIF16ColorVal *pixelL, FLIF16ColorVal *pixelU)
+{
+    int temp;
+    int exists;
+
+    switch (cb->i) {
+    case 0:
+        if (plane < FLIF16_PLANE_ALPHA)
+        for (int p = 0; p < plane; p++) {
+            if (!ff_colorbuckets_exists(cb, p, pixelL, pixelU)) {
+                return 1;
+            }
+        }
+        cb->smin = 0;
+        cb->smax = 0;
+        cb->i = 1;
+
+    case 1:
+        transform_colorbuckets_minmax(src_ctx, plane,
+                                      pixelL, pixelU,
+                                      &cb->smin, &cb->smax);
+        RAC_GET(rc, &chancectx[0], 0, 1, &exists, FLIF16_RAC_GNZ_INT);
+        if (exists == 0) {
+            cb->i = 0;
+            return 1; // Empty bucket
+        }
+        if (cb->smin == cb->smax) {
+            b->min = cb->smin;
+            b->max = cb->smin;
+            b->discrete = 0;
+            cb->i = 0;
+            return 1;
+        }
+        cb->i = 2;
+
+    case 2:
+        RAC_GET(rc, &chancectx[1], cb->smin, cb->smax, &b->min, FLIF16_RAC_GNZ_INT);
+        cb->i = 3;
+
+    case 3:
+        RAC_GET(rc, &chancectx[2], b->min, cb->smax, &b->max, FLIF16_RAC_GNZ_INT);
+        if (b->min == b->max) {
+            b->discrete = 0;
+            cb->i = 0;
+            return 1;
+        }
+        if (b->min + 1 == b->max) {
+            b->discrete = 0;
+            cb->i = 0;
+            return 1;
+        }
+        cb->i = 4;
+
+    case 4:
+        RAC_GET(rc, &chancectx[3], 0, 1, &b->discrete, FLIF16_RAC_GNZ_INT);
+        cb->i = 5;
+    }
+
+    if (b->discrete) {
+        switch (cb->i) {
+        case 5:
+            RAC_GET(rc, &chancectx[4], 2,
+                    FFMIN(max_per_colorbucket[plane], b->max - b->min),
+                    &cb->nb, FLIF16_RAC_GNZ_INT);
+            b->values = 0;
+            b->values = av_mallocz(sizeof(*b->values));
+            if (!b->values)
+                return AVERROR(ENOMEM);
+            b->values_last = b->values;
+            b->values->data = b->min;
+            b->values_size++;
+
+            cb->v = b->min;
+            cb->i2 = 1;
+            cb->i = 6;
+
+        case 6:
+            for (; cb->i2 < cb->nb - 1; cb->i2++) {
+                RAC_GET(rc, &chancectx[5], cb->v + 1,
+                        b->max + 1 - cb->nb + cb->i2, &temp,
+                        FLIF16_RAC_GNZ_INT);
+                b->values_last->next = av_mallocz(sizeof(*b->values_last->next));
+                if (!b->values_last->next)
+                    return AVERROR(ENOMEM);
+                b->values_last = b->values_last->next;
+                b->values_last->data = temp;
+                b->values_size++;
+                cb->v = temp;
+            }
+            b->values_last->next = NULL;
+            b->values_size = cb->nb - 1;
+
+            if (b->min < b->max) {
+                b->values_last->next = av_mallocz(sizeof(*b->values_last->next));
+                if (!b->values_last->next)
+                    return AVERROR(ENOMEM);
+                b->values_last = b->values_last->next;
+                b->values_last->data = b->max;
+                b->values_last->next = NULL;
+                b->values_size++;
+            }
+        }
+    }
+
+    cb->i = 0;
+    cb->i2 = 0;
+    cb->nb = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static int transform_colorbuckets_read(FLIF16TransformContext *ctx,
+                                       FLIF16Context *dec_ctx,
+                                       FLIF16RangesContext *src_ctx)
+{
+    TransformPrivColorbuckets *data = ctx->priv_data;
+    ColorBuckets *cb = data->cb;
+    int8_t ret;
+
+    switch (data->i) {
+    case 0:
+        ret = ff_load_bucket(&dec_ctx->rc, data->ctx, &cb->bucket0, cb,
+                             src_ctx, 0, data->pixelL, data->pixelU);
+        if (ret <= 0)
+            return AVERROR(EAGAIN);
+        data->pixelL[0] = cb->min0;
+        data->pixelU[0] = cb->min0;
+        data->i = 1;
+
+    case 1:
+        for (; data->j < cb->bucket1_size; data->j++) {
+            ret = ff_load_bucket(&dec_ctx->rc, data->ctx,
+                                 &cb->bucket1[data->j], cb,
+                                 src_ctx, 1, data->pixelL, data->pixelU);
+            if (ret <= 0)
+                return AVERROR(EAGAIN);
+            data->pixelL[0] += 1;
+            data->pixelU[0] += 1;
+        }
+        data->j = 0;
+
+        if (ff_flif16_ranges_min(src_ctx, 2) < ff_flif16_ranges_max(src_ctx, 2)) {
+            data->pixelL[0] = cb->min0;
+            data->pixelU[0] = cb->min0 + 1 - 1;
+            data->pixelL[1] = cb->min1;
+            data->pixelU[1] = cb->min1 + 4 - 1;
+            data->i = 2;
+        } else
+            data->i = 3;
+    }
+
+    switch (data->i) {
+        for (; data->j < cb->bucket2_size; data->j++) {
+            data->pixelL[1] = cb->min1;
+            data->pixelU[1] = cb->min1 + 4 - 1;
+    case 2:
+            for (; data->k < cb->bucket2_list_size; data->k++) {
+                ret = ff_load_bucket(&dec_ctx->rc, data->ctx,
+                                     &cb->bucket2[data->j][data->k], cb,
+                                     src_ctx, 2, data->pixelL, data->pixelU);
+                if (ret <= 0)
+                    return AVERROR(EAGAIN);
+                data->pixelL[1] += 4;
+                data->pixelU[1] += 4;
+            }
+            data->k = 0;
+            data->pixelL[0] += 1;
+            data->pixelU[0] += 1;
+        }
+        data->j = 0;
+        data->i = 3;
+
+    case 3:
+        if (src_ctx->num_planes > 3) {
+            ret = ff_load_bucket(&dec_ctx->rc, data->ctx, &cb->bucket3, cb,
+                                 src_ctx, 3, data->pixelL, data->pixelU);
+            if (ret <= 0)
+                return AVERROR(EAGAIN);
+        }
+
+    }
+
+    data->i = 0;
+    data->j = 0;
+    data->k = 0;
+    return 1;
+}
+
+static void transform_colorbuckets_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivColorbuckets *data = ctx->priv_data;
+    ff_priv_colorbuckets_close(data->cb);
+    av_freep(&data->cb);
+}
+
+static int transform_framedup_init(FLIF16TransformContext *ctx,
+                                   FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramedup *data = ctx->priv_data;
+    ff_flif16_chancecontext_init(&data->chancectx);
+    data->i = 0;
+
+    return 1;
+}
+
+static void transform_framedup_configure(FLIF16TransformContext *ctx,
+                                         const int setting)
+{
+    TransformPrivFramedup *data = ctx->priv_data;
+    data->nb = setting;
+}
+
+static int transform_framedup_read(FLIF16TransformContext  *ctx,
+                                   FLIF16Context *dec_ctx,
+                                   FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramedup *data = ctx->priv_data;
+
+    switch (ctx->i) {
+    case 0:
+        data->seen_before = av_malloc_array(data->nb, sizeof(*data->seen_before));
+        if (!data->seen_before)
+            return 0;
+        data->seen_before[0] = -1;
+        ctx->i = 1;
+        data->i = 1;
+
+    case 1:
+        for (; data->i < data->nb; data->i++) {
+            RAC_GET(&dec_ctx->rc, &data->chancectx, -1, data->i - 1,
+                    &data->seen_before[data->i], FLIF16_RAC_NZ_INT);
+        }
+        data->i = 0;
+        return 1;
+    }
+
+    ctx->i = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_framedup_meta(FLIF16Context *ctx,
+                                                    FLIF16PixelData *frame,
+                                                    uint32_t frame_count,
+                                                    FLIF16TransformContext *t_ctx,
+                                                    FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramedup *data = t_ctx->priv_data;
+    for (unsigned int fr = 0; fr < frame_count; fr++) {
+        frame[fr].seen_before = data->seen_before[fr];
+    }
+
+    return src_ctx;
+}
+
+static void transform_framedup_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivFramedup *data = ctx->priv_data;
+    av_freep(&data->seen_before);
+}
+
+static int transform_frameshape_init(FLIF16TransformContext *ctx,
+                                     FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFrameshape *data = ctx->priv_data;
+    ff_flif16_chancecontext_init(&data->chancectx);
+    data->i = 0;
+
+    return 1;
+}
+
+static void transform_frameshape_configure(FLIF16TransformContext *ctx,
+                                           const int setting)
+{
+    TransformPrivFrameshape *data = ctx->priv_data;
+    if (data->nb == 0) {
+        data->nb = setting;
+    } else
+        data->cols = setting;
+}
+
+static int transform_frameshape_read(FLIF16TransformContext  *ctx,
+                                        FLIF16Context *dec_ctx,
+                                        FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFrameshape *data = ctx->priv_data;
+    int temp;
+
+    switch (ctx->i) {
+    case 0:
+        data->b = av_malloc_array(data->nb, sizeof(*data->b));
+        if (!data->b)
+            return AVERROR(ENOMEM);
+        data->e = av_malloc_array(data->nb, sizeof(*data->e));
+        if (!data->e) {
+            av_freep(&data->b);
+            return AVERROR(ENOMEM);
+        }
+        ctx->i = 1;
+
+    case 1:
+        for (; data->i < data->nb; data->i++) {
+            RAC_GET(&dec_ctx->rc, &data->chancectx, 0, data->cols,
+                    &data->b[data->i], FLIF16_RAC_NZ_INT);
+        }
+        ctx->i = 2;
+        data->i = 0;
+
+    case 2:
+        for (; data->i < data->nb; data->i++) {
+            temp = ff_flif16_rac_process(&dec_ctx->rc, &data->chancectx, 0,
+                                         data->cols - data->b[data->i],
+                                         &data->e[data->i], FLIF16_RAC_NZ_INT);
+            if (temp == 0)
+                return AVERROR(EAGAIN);
+            data->e[data->i] = data->cols - data->e[data->i];
+
+            if (data->e[data->i] > data->cols       ||
+                data->e[data->i] < data->b[data->i] ||
+                data->e[data->i] <= 0)
+                    return 0;
+        }
+        data->i = 0;
+    }
+
+    ctx->i = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_frameshape_meta(FLIF16Context *ctx,
+                                                      FLIF16PixelData *frame,
+                                                      uint32_t frame_count,
+                                                      FLIF16TransformContext *t_ctx,
+                                                      FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFrameshape *data = t_ctx->priv_data;
+    uint32_t pos = 0;
+
+    for (unsigned int fr = 1; fr < frame_count; fr++) {
+        if (frame[fr].seen_before >= 0)
+            continue;
+        frame[fr].col_begin = av_malloc_array(ctx->height, sizeof(*frame->col_begin));
+        if (!frame[fr].col_begin) {
+            return NULL;
+        }
+        frame[fr].col_end   = av_malloc_array(ctx->height, sizeof(*frame->col_end));
+        if (!frame[fr].col_end) {
+            av_freep(&frame[fr].col_begin);
+            return NULL;
+        }
+        for (uint32_t r = 0; r < ctx->height; r++) {
+            av_assert1(pos < data->nb);
+            frame[fr].col_begin[r] = data->b[pos];
+            frame[fr].col_end[r] = data->e[pos];
+            pos++;
+        }
+    }
+
+    return src_ctx;
+}
+
+static void transform_frameshape_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivFrameshape *data = ctx->priv_data;
+    av_freep(&data->b);
+    av_freep(&data->e);
+}
+
+static int transform_framecombine_init(FLIF16TransformContext *ctx,
+                                       FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramecombine *data = ctx->priv_data;
+    ff_flif16_chancecontext_init(&data->chancectx);
+    return 1;
+}
+
+static void transform_framecombine_configure(FLIF16TransformContext *ctx,
+                                             const int setting)
+{
+    TransformPrivFramecombine *data = ctx->priv_data;
+    data->user_max_lookback = data->nb_frames = setting;
+}
+
+static int transform_framecombine_read(FLIF16TransformContext *ctx,
+                                       FLIF16Context *dec_ctx,
+                                       FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramecombine *data = ctx->priv_data;
+
+    switch (ctx->i) {
+    case 0:
+        if (src_ctx->num_planes > 4)
+            return 0;
+        ctx->i = 1;
+
+    case 1:
+        RAC_GET(&dec_ctx->rc, &data->chancectx, 1, data->nb_frames - 1,
+                    &data->max_lookback, FLIF16_RAC_GNZ_INT);
+    }
+
+    ctx->i = 0;
+    return 1;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static FLIF16RangesContext *transform_framecombine_meta(FLIF16Context *ctx,
+                                                        FLIF16PixelData *frame,
+                                                        uint32_t frame_count,
+                                                        FLIF16TransformContext *t_ctx,
+                                                        FLIF16RangesContext *src_ctx)
+{
+    TransformPrivFramecombine *data = t_ctx->priv_data;
+    RangesPrivFramecombine *rdata;
+    FLIF16RangesContext *ranges;
+    int lookback;
+
+    ranges = av_mallocz(sizeof(*ranges));
+    if (!ranges)
+        return NULL;
+    rdata = av_mallocz(sizeof(*rdata));
+    if (!rdata) {
+        av_freep(&ranges);
+        return NULL;
+    }
+    av_assert0(data->max_lookback < frame_count);
+    data->was_greyscale = (src_ctx->num_planes < 2);
+    data->was_flat = (src_ctx->num_planes < 4);
+
+    data->orig_num_planes = ctx->num_planes;
+    ctx->num_planes = 5;
+
+    lookback = frame_count - 1;
+    if (lookback > data->max_lookback)
+        lookback = data->max_lookback;
+
+    ranges->r_no = FLIF16_RANGES_FRAMELOOKBACK;
+    ranges->num_planes = 5;
+    ranges->priv_data = rdata;
+
+    rdata->num_prev_frames = lookback;
+    rdata->alpha_min = (src_ctx->num_planes == 4 ? ff_flif16_ranges_min(src_ctx, 3) : 1);
+    rdata->alpha_max = (src_ctx->num_planes == 4 ? ff_flif16_ranges_max(src_ctx, 3) : 1);
+    rdata->ranges = src_ctx;
+
+    return ranges;
+}
+
+static void transform_framecombine_reverse(FLIF16Context *ctx,
+                                          FLIF16TransformContext *t_ctx,
+                                          FLIF16PixelData *frame,
+                                          uint32_t stride_row,
+                                          uint32_t stride_col)
+{
+    TransformPrivFramecombine *data = t_ctx->priv_data;
+    ctx->num_planes = data->orig_num_planes;
+}
+
+const FLIF16Transform flif16_transform_channelcompact = {
+    .priv_data_size = sizeof(TransformPrivChannelcompact),
+    .init           = &transform_channelcompact_init,
+    .read           = &transform_channelcompact_read,
+    .meta           = &transform_channelcompact_meta,
+    .reverse        = &transform_channelcompact_reverse,
+    .close          = &transform_channelcompact_close
+};
+
+const FLIF16Transform flif16_transform_ycocg = {
+    .priv_data_size = sizeof(TransformPrivYCoCg),
+    .init           = &transform_ycocg_init,
+    .read           = NULL,
+    .meta           = &transform_ycocg_meta,
+    .reverse        = &transform_ycocg_reverse,
+    .close          = NULL
+};
+
+const FLIF16Transform flif16_transform_permuteplanes = {
+    .priv_data_size = sizeof(TransformPrivPermuteplanes),
+    .init           = &transform_permuteplanes_init,
+    .read           = &transform_permuteplanes_read,
+    .meta           = &transform_permuteplanes_meta,
+    .reverse        = &transform_permuteplanes_reverse,
+    .close          = NULL
+};
+
+const FLIF16Transform flif16_transform_bounds = {
+    .priv_data_size = sizeof(TransformPrivBounds),
+    .init           = &transform_bounds_init,
+    .read           = &transform_bounds_read,
+    .meta           = &transform_bounds_meta,
+    .reverse        = NULL,
+    .close          = NULL
+};
+
+const FLIF16Transform flif16_transform_palette = {
+    .priv_data_size = sizeof(TransformPrivPalette),
+    .init           = &transform_palette_init,
+    .read           = &transform_palette_read,
+    .meta           = &transform_palette_meta,
+    .reverse        = &transform_palette_reverse,
+    .close          = &transform_palette_close
+};
+
+const FLIF16Transform flif16_transform_palettealpha = {
+    .priv_data_size = sizeof(TransformPrivPalettealpha),
+    .init           = &transform_palettealpha_init,
+    .read           = &transform_palettealpha_read,
+    .meta           = &transform_palettealpha_meta,
+    .configure      = &transform_palettealpha_configure,
+    .reverse        = &transform_palettealpha_reverse,
+    .close          = &transform_palettealpha_close
+};
+
+const FLIF16Transform flif16_transform_colorbuckets = {
+    .priv_data_size = sizeof(TransformPrivColorbuckets),
+    .init           = &transform_colorbuckets_init,
+    .read           = &transform_colorbuckets_read,
+    .meta           = &transform_colorbuckets_meta,
+    .reverse        = NULL,
+    .close          = &transform_colorbuckets_close
+};
+
+const FLIF16Transform flif16_transform_framedup = {
+    .priv_data_size = sizeof(TransformPrivFramedup),
+    .init           = &transform_framedup_init,
+    .read           = &transform_framedup_read,
+    .meta           = &transform_framedup_meta,
+    .configure      = &transform_framedup_configure,
+    .reverse        = NULL,
+    .close          = &transform_framedup_close
+};
+
+const FLIF16Transform flif16_transform_frameshape = {
+    .priv_data_size = sizeof(TransformPrivFrameshape),
+    .init           = &transform_frameshape_init,
+    .read           = &transform_frameshape_read,
+    .meta           = &transform_frameshape_meta,
+    .configure      = &transform_frameshape_configure,
+    .reverse        = NULL,
+    .close          = &transform_frameshape_close
+};
+
+const FLIF16Transform flif16_transform_framecombine = {
+    .priv_data_size = sizeof(TransformPrivFramecombine),
+    .init           = &transform_framecombine_init,
+    .read           = &transform_framecombine_read,
+    .meta           = &transform_framecombine_meta,
+    .configure      = &transform_framecombine_configure,
+    .reverse        = &transform_framecombine_reverse,
+    .close          = NULL
+};
+
+const FLIF16Transform *flif16_transforms[13] = {
+    [FLIF16_TRANSFORM_CHANNELCOMPACT] = &flif16_transform_channelcompact,
+    [FLIF16_TRANSFORM_YCOCG]          = &flif16_transform_ycocg,
+    [FLIF16_TRANSFORM_RESERVED1]      = NULL,
+    [FLIF16_TRANSFORM_PERMUTEPLANES]  = &flif16_transform_permuteplanes,
+    [FLIF16_TRANSFORM_BOUNDS]         = &flif16_transform_bounds,
+    [FLIF16_TRANSFORM_PALETTEALPHA]   = &flif16_transform_palettealpha,
+    [FLIF16_TRANSFORM_PALETTE]        = &flif16_transform_palette,
+    [FLIF16_TRANSFORM_COLORBUCKETS]   = &flif16_transform_colorbuckets,
+    [FLIF16_TRANSFORM_RESERVED2]      = NULL,
+    [FLIF16_TRANSFORM_RESERVED3]      = NULL,
+    [FLIF16_TRANSFORM_DUPLICATEFRAME] = &flif16_transform_framedup,
+    [FLIF16_TRANSFORM_FRAMESHAPE]     = &flif16_transform_frameshape,
+    [FLIF16_TRANSFORM_FRAMELOOKBACK]  = &flif16_transform_framecombine
+};
+
+FLIF16TransformContext *ff_flif16_transform_init(int t_no, FLIF16RangesContext *r_ctx)
+{
+    const FLIF16Transform *trans;
+    FLIF16TransformContext *ctx;
+    void *k = NULL;
+
+    trans = flif16_transforms[t_no];
+    if (!trans)
+        return NULL;
+    ctx = av_mallocz(sizeof(*ctx));
+    if (!ctx)
+        return NULL;
+    if (trans->priv_data_size) {
+        k = av_mallocz(trans->priv_data_size);
+        if (!k) {
+            av_freep(&ctx);
+            return NULL;
+        }
+    }
+    ctx->t_no      = t_no;
+    ctx->priv_data = k;
+    ctx->segment   = 0;
+    ctx->i         = 0;
+
+    if (trans->init)
+        if (!trans->init(ctx, r_ctx))
+            return NULL;
+
+    return ctx;
+}
+
+int ff_flif16_transform_read(FLIF16Context *dec_ctx,
+                             FLIF16TransformContext *ctx,
+                             FLIF16RangesContext *r_ctx)
+{
+    const FLIF16Transform *trans = flif16_transforms[ctx->t_no];
+    if (trans->read)
+        return trans->read(ctx, dec_ctx, r_ctx);
+    else
+        return 1;
+}
+
+FLIF16RangesContext *ff_flif16_transform_meta(FLIF16Context *ctx,
+                                              FLIF16PixelData *frames,
+                                              uint32_t frames_count,
+                                              FLIF16TransformContext *t_ctx,
+                                              FLIF16RangesContext *r_ctx)
+{
+    const FLIF16Transform *trans;
+    trans = flif16_transforms[t_ctx->t_no];
+    if (trans->meta)
+        return trans->meta(ctx, frames, frames_count, t_ctx, r_ctx);
+    else
+        return r_ctx;
+}
+
+void ff_flif16_transform_configure(FLIF16TransformContext *ctx, const int setting)
+{
+    const FLIF16Transform *trans = flif16_transforms[ctx->t_no];
+    if (trans->configure)
+        trans->configure(ctx, setting);
+}
+
+void ff_flif16_transform_reverse(FLIF16Context *ctx, FLIF16TransformContext *t_ctx,
+                                 FLIF16PixelData *frame, uint8_t stride_row,
+                                 uint8_t stride_col)
+{
+    const FLIF16Transform *trans = flif16_transforms[t_ctx->t_no];
+    if (trans->reverse != NULL)
+        trans->reverse(ctx, t_ctx, frame, stride_row, stride_col);
+}
+
+void ff_flif16_transforms_close(FLIF16TransformContext *ctx)
+{
+    const FLIF16Transform *trans = flif16_transforms[ctx->t_no];
+    if (trans->close)
+        trans->close(ctx);
+    if (trans->priv_data_size)
+        av_freep(&ctx->priv_data);
+    av_freep(&ctx);
+}
diff --git a/libavcodec/flif16_transform.h b/libavcodec/flif16_transform.h
new file mode 100644
index 0000000000..fc5d6ee2a1
--- /dev/null
+++ b/libavcodec/flif16_transform.h
@@ -0,0 +1,124 @@
+/*
+ * Transforms for FLIF16
+ * Copyright (c) 2020 Kartik K. Khullar <kartikkhullar840 at gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Transforms for FLIF16.
+ */
+
+#ifndef AVCODEC_FLIF16_TRANSFORM_H
+#define AVCODEC_FLIF16_TRANSFORM_H
+
+#include "avcodec.h"
+#include "libavutil/common.h"
+#include "flif16.h"
+
+typedef enum FLIF16RangesTypes {
+    FLIF16_RANGES_CHANNELCOMPACT,
+    FLIF16_RANGES_YCOCG,
+    FLIF16_RANGES_PERMUTEPLANES,
+    FLIF16_RANGES_PERMUTEPLANESSUBTRACT,
+    FLIF16_RANGES_BOUNDS,
+    FLIF16_RANGES_STATIC,
+    FLIF16_RANGES_PALETTEALPHA,
+    FLIF16_RANGES_PALETTE,
+    FLIF16_RANGES_COLORBUCKETS,
+    FLIF16_RANGES_FRAMELOOKBACK
+} FLIF16RangesTypes;
+
+typedef enum FLIF16TransformsTypes {
+    FLIF16_TRANSFORM_CHANNELCOMPACT,
+    FLIF16_TRANSFORM_YCOCG,
+    FLIF16_TRANSFORM_RESERVED1,
+    FLIF16_TRANSFORM_PERMUTEPLANES,
+    FLIF16_TRANSFORM_BOUNDS,
+    FLIF16_TRANSFORM_PALETTEALPHA,
+    FLIF16_TRANSFORM_PALETTE,
+    FLIF16_TRANSFORM_COLORBUCKETS,
+    FLIF16_TRANSFORM_RESERVED2,
+    FLIF16_TRANSFORM_RESERVED3,
+    FLIF16_TRANSFORM_DUPLICATEFRAME,
+    FLIF16_TRANSFORM_FRAMESHAPE,
+    FLIF16_TRANSFORM_FRAMELOOKBACK,
+} FLIF16TransformsTypes;
+
+extern const FLIF16Ranges *flif16_ranges[];
+extern const FLIF16Transform *flif16_transforms[MAX_TRANSFORMS];
+
+FLIF16RangesContext *ff_flif16_ranges_static_init(uint8_t num_planes,
+                                                  uint32_t bpc);
+
+void ff_flif16_ranges_close(FLIF16RangesContext* r_ctx);
+
+static inline FLIF16ColorVal ff_flif16_ranges_min(FLIF16RangesContext *r_ctx, int p)
+{
+    const FLIF16Ranges *ranges = flif16_ranges[r_ctx->r_no];
+    if (ranges->min)
+        return ranges->min(r_ctx, p);
+    else
+        return 0;
+}
+
+static inline FLIF16ColorVal ff_flif16_ranges_max(FLIF16RangesContext *r_ctx, int p)
+{
+    const FLIF16Ranges *ranges = flif16_ranges[r_ctx->r_no];
+    if (ranges->max)
+        return ranges->max(r_ctx, p);
+    else
+        return 0;
+}
+
+static inline void ff_flif16_ranges_minmax(FLIF16RangesContext *r_ctx, int p,
+                                           FLIF16ColorVal *prev_planes,
+                                           FLIF16ColorVal *minv, FLIF16ColorVal *maxv)
+{
+    flif16_ranges[r_ctx->r_no]->minmax(r_ctx, p, prev_planes, minv, maxv);
+}
+
+static inline void ff_flif16_ranges_snap(FLIF16RangesContext *r_ctx, int p,
+                                         FLIF16ColorVal *prev_planes, FLIF16ColorVal *minv,
+                                         FLIF16ColorVal *maxv, FLIF16ColorVal *v)
+{
+    flif16_ranges[r_ctx->r_no]->snap(r_ctx, p, prev_planes, minv, maxv, v);
+}
+
+FLIF16TransformContext *ff_flif16_transform_init(int t_no,
+                                                 FLIF16RangesContext *ranges);
+
+void ff_flif16_transform_configure(FLIF16TransformContext *t_ctx,
+                                   const int setting);
+
+int ff_flif16_transform_read(FLIF16Context *ctx, FLIF16TransformContext *t_ctx,
+                             FLIF16RangesContext *ranges);
+
+FLIF16RangesContext* ff_flif16_transform_meta(FLIF16Context *ctx,
+                                              FLIF16PixelData *frame,
+                                              uint32_t frame_count,
+                                              FLIF16TransformContext *t_ctx,
+                                              FLIF16RangesContext *ranges);
+
+void ff_flif16_transform_reverse(FLIF16Context *ctx, FLIF16TransformContext *t_ctx,
+                                 FLIF16PixelData *frame, uint8_t stride_row,
+                                 uint8_t stride_col);
+
+void ff_flif16_transforms_close(FLIF16TransformContext *t_ctx);
+
+#endif /* FLIF16_TRANSFORM_H */
diff --git a/libavcodec/flif16dec.c b/libavcodec/flif16dec.c
new file mode 100644
index 0000000000..44ea96011a
--- /dev/null
+++ b/libavcodec/flif16dec.c
@@ -0,0 +1,1764 @@
+/*
+ * FLIF16 Decoder
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui at teknik.io>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * FLIF16 Decoder
+*/
+
+#include "flif16.h"
+#include "flif16_rangecoder.h"
+#include "flif16_transform.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+#include "libavutil/common.h"
+#include "libavutil/imgutils.h"
+
+typedef enum FLIF16DecodeStates {
+    FLIF16_HEADER = 0,
+    FLIF16_SECONDHEADER,
+    FLIF16_TRANSFORM,
+    FLIF16_ROUGH_PIXELDATA,
+    FLIF16_MANIAC,
+    FLIF16_PIXELDATA,
+    FLIF16_OUTPUT,
+    FLIF16_EOS
+} FLIF16DecodeStates;
+
+/*
+ * Due to the nature of the format, the decoder has to take the entirety of the
+ * data before it can generate any frames. The decoder has to return
+ * AVERROR(EAGAIN) as long as the bitstream is incomplete.
+ */
+
+typedef struct FLIF16DecoderContext {
+
+    /* Inheritance from FLIF16Context */
+
+    FLIF16MANIACContext maniac_ctx;
+    FLIF16RangeCoder rc;
+
+    // Dimensions
+    uint32_t width;
+    uint32_t height;
+    uint32_t num_frames;
+    uint32_t meta;       ///< Size of a meta chunk
+
+    // Primary Header
+    uint32_t bpc;         ///< 2 ^ Bytes per channel
+    uint16_t *framedelay; ///< Frame delay for each frame
+    uint8_t  ia;          ///< Is image interlaced or/and animated or not
+    uint8_t  num_planes;  ///< Number of planes
+    uint8_t  loops;       ///< Number of times animation loops
+    FLIF16PlaneMode plane_mode[MAX_PLANES];
+
+    // Transform flags
+    uint8_t framedup;
+    uint8_t frameshape;
+    uint8_t framelookback;
+
+    /* End Inheritance from FLIF16Context */
+
+    AVFrame *out_frame;
+    FLIF16PixelData  *frames;
+    GetByteContext gb;
+    int64_t pts;
+    uint32_t out_frames_count;
+
+    FLIF16DecodeStates state; ///< The section of the file the parser is in currently.
+    unsigned int segment;     ///< The "segment" the code is supposed to jump to
+    unsigned int segment2;
+    int i;                    ///< A generic iterator used to save states between for loops.
+    int i2;
+    int i3;
+
+    // Secondary Header
+    uint8_t alphazero;   ///< Alphazero Flag
+    uint8_t custombc;    ///< Custom Bitchance Flag
+    uint32_t alpha;      ///< Chancetable custom alphadivisor
+    uint8_t customalpha; ///< Custom alphadiv & cutoff flag
+    uint8_t cut;         ///< Chancetable custom cutoff
+    uint8_t ipp;         ///< Invisible pixel predictor
+
+    // Transforms
+    uint8_t transform_top;
+    FLIF16TransformContext *transforms[MAX_TRANSFORMS];
+    FLIF16RangesContext *range; ///< The minimum and maximum values a
+                                ///  channel's pixels can take. Changes
+                                ///  depending on transformations applied
+
+    // MANIAC Trees
+    FLIF16MinMax prop_ranges[MAX_PROP_RANGES]; ///< Property Ranges
+    uint32_t prop_ranges_size;
+
+    // Pixeldata
+    FLIF16ColorVal grays[MAX_PLANES];
+    FLIF16ColorVal properties[MAX_PROPERTIES];
+    FLIF16ColorVal guess;    ///< State variable. Stores guess
+    FLIF16ColorVal min, max;
+    uint32_t begin;          ///< State variable for Column range end
+    uint32_t end;            ///< State variable for Column range start
+    uint32_t c;              ///< State variable for current column
+    uint8_t curr_plane;      ///< State variable. Current plane under processing
+
+    // Interlaced Pixeldata
+    uint8_t default_order;
+    int begin_zl;
+    int rough_zl;
+    int end_zl;
+    int curr_zoom;
+    int zoomlevels[MAX_PLANES];
+    int predictors[MAX_PLANES];
+    int predictor;
+} FLIF16DecoderContext;
+
+// Cast values to FLIF16Context for some functions.
+#define CTX_CAST(x) ((FLIF16Context *) (x))
+
+#define PIXEL_SET(ctx, fr, p, r, c, val) ff_flif16_pixel_set(CTX_CAST(ctx), &(ctx)->frames[fr], p, r, c, val)
+#define PIXEL_GET(ctx, fr, p, r, c) ff_flif16_pixel_get(CTX_CAST(ctx), &(ctx)->frames[fr], p, r, c)
+#define PIXEL_SETZ(ctx, fr, p, z, r, c, val) ff_flif16_pixel_setz(CTX_CAST(ctx), &(ctx)->frames[fr], p, z, r, c, val)
+#define PIXEL_GETZ(ctx, fr, p, z, r, c) ff_flif16_pixel_getz(CTX_CAST(ctx), &(ctx)->frames[fr], p, z, r, c)
+#define PIXEL_GETFAST(ctx, fr, p, r, c) ff_flif16_pixel_get_fast(CTX_CAST(ctx), &(ctx)->frames[fr], p, r, c)
+#define PIXEL_SETFAST(ctx, fr, p, r, c, val) ff_flif16_pixel_set_fast(CTX_CAST(ctx), &(ctx)->frames[fr], p, r, c, val)
+
+#define PREV_FRAME(frames, f_no) (((frames)[(f_no) - 1].seen_before >= 0) ? &(frames)[(frames)[(f_no) - 1].seen_before] : &(frames)[(f_no) - 1])
+#define PREV_FRAMENUM(frames, f_no) (((frames)[(f_no) - 1].seen_before >= 0) ? (frames)[(f_no) - 1].seen_before : (f_no) - 1)
+#define LOOKBACK_FRAMENUM(ctx, frames, f_no, r, c) (((frames)[(f_no) - PIXEL_GET((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (r), (c))].seen_before >= 0) ? \
+                                                    ((frames)[(f_no) - PIXEL_GET((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (r), (c))].seen_before) : \
+                                                    ((f_no) - PIXEL_GET((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (r), (c))))
+#define LOOKBACK_FRAMENUMZ(ctx, frames, f_no, z, r, c) (((frames)[(f_no) - PIXEL_GETZ((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (z), (r), (c))].seen_before >= 0) ? \
+                                                       ((frames)[(f_no) - PIXEL_GETZ((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (z), (r), (c))].seen_before) : \
+                                                       ((f_no) - PIXEL_GETZ((ctx), (f_no), FLIF16_PLANE_LOOKBACK, (z), (r), (c))))
+
+#define IS_CONSTANT(ranges, plane) (ff_flif16_ranges_min((ranges), (plane)) >= \
+                                    ff_flif16_ranges_max((ranges), (plane)))
+
+/*
+ * From reference decoder:
+ *
+ * The order in which the planes are encoded.
+ * 0: lookback (Lookback) (animations-only, value refers to a previous frame) has
+ *    to be first, because all other planes are not encoded if lookback != 0
+ * 1: Alpha has to be next, because for fully transparent A=0 pixels, the other
+ *    planes are not encoded
+ * 2: Y (luma) is next (the first channel for still opaque images), because it is
+ *    perceptually most important
+ * 3, 4: Co and Cg are in that order because Co is perceptually slightly more
+ *       important than Cg [citation needed]
+ */
+static const int plane_ordering[] = {
+    FLIF16_PLANE_LOOKBACK,
+    FLIF16_PLANE_ALPHA,
+    FLIF16_PLANE_Y,
+    FLIF16_PLANE_CO,
+    FLIF16_PLANE_CG
+};
+
+static int flif16_read_header(AVCodecContext *avctx)
+{
+    int ret;
+    uint8_t temp, count = 4;
+    uint8_t header[4];
+    FLIF16DecoderContext *s = avctx->priv_data;
+    uint32_t *vlist[] = { &s->width, &s->height, &s->num_frames };
+
+    s->cut   = CHANCETABLE_DEFAULT_CUT;
+    s->alpha = CHANCETABLE_DEFAULT_ALPHA;
+
+    // Minimum size has been empirically found to be 8 bytes.
+    if (bytestream2_size(&s->gb) < 8) {
+        av_log(avctx, AV_LOG_ERROR, "buf size too small (%d)\n",
+               bytestream2_size(&s->gb));
+        return AVERROR_INVALIDDATA;
+    }
+
+    bytestream2_get_bufferu(&s->gb, header, 4);
+
+    if (memcmp(header, flif16_header, 4)) {
+        av_log(avctx, AV_LOG_ERROR, "bad magic number\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->state = FLIF16_HEADER;
+
+    temp = bytestream2_get_byte(&s->gb);
+    s->ia         = temp >> 4;
+    s->num_planes = (0x0F & temp);
+
+    s->bpc = bytestream2_get_byte(&s->gb);
+
+
+
+    // Handle dimensions and frames
+    for(int i = 0; i < 2 + ((s->ia > 4) ? 1 : 0); i++) {
+        while ((temp = bytestream2_get_byte(&s->gb)) > 127) {
+            VARINT_APPEND(*vlist[i], temp);
+            if (!(count--)) {
+                return AVERROR(ENOMEM);
+            }
+        }
+        VARINT_APPEND(*vlist[i], temp);
+        count = 4;
+    }
+
+    s->width++;
+    s->height++;
+    (s->ia > 4) ? (s->num_frames += 2) : (s->num_frames = 1);
+
+    // Check for multiplication overflow
+    if ((ret = av_image_check_size2(s->width, s->height, avctx->max_pixels,
+        AV_PIX_FMT_NONE, 0, avctx)) < 0)
+        return ret;
+
+    if (s->num_frames > 1) {
+        s->framedelay = av_malloc_array(s->num_frames, sizeof(*(s->framedelay)));
+        if (!s->framedelay)
+            return AVERROR(ENOMEM);
+    }
+
+    s->frames = ff_flif16_frames_init(s->num_frames);
+
+    if (!s->frames)
+        return AVERROR(ENOMEM);
+
+    // Handle Metadata Chunk
+    while ((temp = bytestream2_get_byte(&s->gb)) != 0) {
+        bytestream2_seek(&s->gb, 3, SEEK_CUR);
+        while ((temp = bytestream2_get_byte(&s->gb)) > 127) {
+            VARINT_APPEND(s->meta, temp);
+            if (!(count--)) {
+                return AVERROR(ENOMEM);
+            }
+        }
+        VARINT_APPEND(s->meta, temp);
+        bytestream2_seek(&s->gb, s->meta, SEEK_CUR);
+        count = 4;
+    }
+
+    s->state = FLIF16_SECONDHEADER;
+    return 0;
+}
+
+static int flif16_read_second_header(AVCodecContext *avctx)
+{
+    int ret;
+    uint32_t temp;
+    FLIF16DecoderContext *s = avctx->priv_data;
+
+    switch (s->segment) {
+    case 0:
+        if ((ret = ff_flif16_rac_init(&s->rc, &s->gb)) < 0)
+            return ret;
+        s->segment++;
+
+    case 1:
+        /*
+         * In original source this is handled in what seems to be a very
+         * bogus manner. It takes all the bpps of all planes and then
+         * takes the max, negating any benefit of actually keeping these
+         * multiple values.
+         */
+        if (s->bpc == '0') {
+            s->bpc = 0;
+            for (; s->i < s->num_planes; s->i++) {
+                RAC_GET(&s->rc, NULL, 1, 15, &temp, FLIF16_RAC_UNI_INT8);
+                s->bpc = FFMAX(s->bpc, (1 << temp) - 1);
+            }
+        } else
+            s->bpc = (s->bpc == '1') ? 255 : 65535;
+        s->i = 0;
+        s->range = ff_flif16_ranges_static_init(s->num_planes, s->bpc);
+        s->segment++;
+
+    case 2:
+        if (s->num_planes > 3) {
+            RAC_GET(&s->rc, NULL, 0, 1, &s->alphazero, FLIF16_RAC_UNI_INT8);
+        }
+        s->segment++;
+
+    case 3:
+        if (s->num_frames > 1) {
+            RAC_GET(&s->rc, NULL, 0, 100, &s->loops, FLIF16_RAC_UNI_INT8);
+        }
+        s->segment++;
+
+    case 4:
+        if (s->num_frames > 1) {
+            for (; (s->i) < (s->num_frames); s->i++) {
+                RAC_GET(&s->rc, NULL, 0, 60000, &(s->framedelay[s->i]),
+                        FLIF16_RAC_UNI_INT16);
+            }
+            s->i = 0;
+        }
+        s->segment++;
+
+    case 5:
+        // Has custom alpha flag
+        RAC_GET(&s->rc, NULL, 0, 1, &s->customalpha, FLIF16_RAC_UNI_INT8);
+        s->segment++;
+
+    case 6:
+        if (s->customalpha) {
+            RAC_GET(&s->rc, NULL, 1, 128, &s->cut, FLIF16_RAC_UNI_INT8);
+        }
+        s->segment++;
+
+    case 7:
+        if (s->customalpha) {
+            RAC_GET(&s->rc, NULL, 2, 128, &s->alpha, FLIF16_RAC_UNI_INT8);
+            s->alpha = 0xFFFFFFFF / s->alpha;
+        }
+        s->segment++;
+
+    case 8:
+        if (s->customalpha)
+            RAC_GET(&s->rc, NULL, 0, 1, &s->custombc, FLIF16_RAC_UNI_INT8);
+        if (s->custombc) {
+            av_log(avctx, AV_LOG_ERROR, "custom bitchances not implemented\n");
+            return AVERROR_PATCHWELCOME;
+        }
+        goto end;
+    }
+
+    end:
+    s->state   = FLIF16_TRANSFORM;
+    s->segment = 0;
+
+#ifdef MULTISCALE_CHANCES_ENABLED
+    s->rc.mct = ff_flif16_multiscale_chancetable_init();
+    ff_flif16_build_log4k_table(&s->rc.log4k);
+#endif
+
+    ff_flif16_chancetable_init(&s->rc.ct, s->alpha, s->cut);
+
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+
+static int flif16_read_transforms(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    FLIF16RangesContext *prev_range;
+    int ret;
+    int unique_frames;
+    FLIF16ColorVal const_plane_value[MAX_PLANES];
+    uint8_t temp;
+
+    switch (s->segment) {
+        while (1) {
+    case 0:
+            RAC_GET(&s->rc, NULL, 0, 0, &temp, FLIF16_RAC_BIT);
+            if(!temp)
+                break;
+            s->segment++;
+
+    case 1:
+            RAC_GET(&s->rc, NULL, 0, MAX_TRANSFORMS, &temp, FLIF16_RAC_UNI_INT8);
+            if (!flif16_transforms[temp]) {
+                av_log(avctx, AV_LOG_ERROR, "transform %u not implemented\n", temp);
+                return AVERROR_PATCHWELCOME;
+            }
+
+            s->transforms[s->transform_top] = ff_flif16_transform_init(temp, s->range);
+            if (!s->transforms[s->transform_top])
+                return AVERROR(ENOMEM);
+
+            switch (temp) {
+            case FLIF16_TRANSFORM_PALETTEALPHA:
+                s->plane_mode[FLIF16_PLANE_ALPHA] = FLIF16_PLANEMODE_CONSTANT;
+                ff_flif16_transform_configure(s->transforms[s->transform_top],
+                                              s->alphazero);
+
+            case FLIF16_TRANSFORM_CHANNELCOMPACT:
+                if (s->num_planes > 3 && !s->plane_mode[FLIF16_PLANE_ALPHA])
+                    s->plane_mode[FLIF16_PLANE_ALPHA] = FLIF16_PLANEMODE_FILL;
+
+            case FLIF16_TRANSFORM_YCOCG:
+            case FLIF16_TRANSFORM_PALETTE:
+                s->plane_mode[FLIF16_PLANE_Y]  = FLIF16_PLANEMODE_NORMAL;
+                s->plane_mode[FLIF16_PLANE_CO] = FLIF16_PLANEMODE_NORMAL;
+                s->plane_mode[FLIF16_PLANE_CG] = FLIF16_PLANEMODE_NORMAL;
+                break;
+
+            case FLIF16_TRANSFORM_DUPLICATEFRAME:
+                s->framedup = 1;
+                if(s->num_frames < 2)
+                     return AVERROR_INVALIDDATA;
+                ff_flif16_transform_configure(s->transforms[s->transform_top],
+                                              s->num_frames);
+                break;
+
+            case FLIF16_TRANSFORM_FRAMESHAPE:
+                s->frameshape = 1;
+                if (s->num_frames < 2)
+                    return AVERROR_INVALIDDATA;
+                unique_frames = s->num_frames - 1;
+                for (unsigned int i = 0; i < s->num_frames; i++) {
+                    if(s->frames[i].seen_before >= 0)
+                        unique_frames--;
+                }
+                if (unique_frames < 1)
+                    return AVERROR_INVALIDDATA;
+                ff_flif16_transform_configure(s->transforms[s->transform_top],
+                                              (unique_frames) * s->height);
+                ff_flif16_transform_configure(s->transforms[s->transform_top],
+                                              s->width);
+                break;
+
+            case FLIF16_TRANSFORM_FRAMELOOKBACK:
+                if(s->num_frames < 2)
+                    return AVERROR_INVALIDDATA;
+                s->framelookback = 1;
+
+                ff_flif16_transform_configure(s->transforms[s->transform_top],
+                                              s->num_frames);
+                break;
+            }
+            s->segment++;
+
+    case 2:
+            if(ff_flif16_transform_read(CTX_CAST(s), s->transforms[s->transform_top],
+                                        s->range) <= 0)
+                goto need_more_data;
+            prev_range = s->range;
+            s->range = ff_flif16_transform_meta(CTX_CAST(s), s->frames, s->num_frames,
+                                                s->transforms[s->transform_top],
+                                                prev_range);
+            if(!s->range)
+                return AVERROR(ENOMEM);
+            s->segment = 0;
+            s->transform_top++;
+        }
+
+    case 3:
+        s->segment = 3;
+        // Read invisible pixel predictor
+        if (s->alphazero && s->num_planes > 3 &&
+            ff_flif16_ranges_min(s->range, 3) <= 0 &&
+            !(s->ia % 2))
+            RAC_GET(&s->rc, NULL, 0, 2, &s->ipp, FLIF16_RAC_UNI_INT8)
+    }
+
+    for (int i = 0; i < FFMIN(s->num_planes, 4); i++) {
+        if (s->plane_mode[i] != FLIF16_PLANEMODE_NORMAL) {
+            if (ff_flif16_ranges_min(s->range, i) >= ff_flif16_ranges_max(s->range, i))
+                const_plane_value[i] = ff_flif16_ranges_min(s->range, i);
+            else
+                s->plane_mode[i] = FLIF16_PLANEMODE_NORMAL;
+        }
+    }
+
+    s->plane_mode[FLIF16_PLANE_LOOKBACK] = FLIF16_PLANEMODE_FILL;
+    const_plane_value[FLIF16_PLANE_LOOKBACK] = 0;
+    for (int i = 0; i < s->num_frames; i++)
+        if ((ret = ff_flif16_planes_init(CTX_CAST(s), &s->frames[i],
+            const_plane_value)) < 0) {
+            return ret;
+        }
+
+    if (!(s->ia % 2))
+        s->state = FLIF16_ROUGH_PIXELDATA;
+    else
+        s->state = FLIF16_MANIAC;
+    s->segment = 0;
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+/**
+ * Used for decoding rough pixeldata
+ */
+static int flif16_blank_maniac_forest_init(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    s->maniac_ctx.forest = av_mallocz((s->num_planes) * sizeof(*(s->maniac_ctx.forest)));
+    if (!s->maniac_ctx.forest)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < s->num_planes; i++) {
+        s->maniac_ctx.forest[i] = av_mallocz(sizeof(*(s->maniac_ctx.forest[i])));
+        if (!s->maniac_ctx.forest[i])
+            return AVERROR(ENOMEM);
+        s->maniac_ctx.forest[i]->data = av_mallocz(sizeof(*(s->maniac_ctx.forest[i]->data)));
+        if (!s->maniac_ctx.forest[i]->data)
+            return AVERROR(ENOMEM);
+        s->maniac_ctx.forest[i]->data[0].property = -1;
+    }
+
+    return 0;
+}
+
+static int flif16_read_maniac_forest(AVCodecContext *avctx)
+{
+    int ret;
+    FLIF16DecoderContext *s = avctx->priv_data;
+
+    if (!s->maniac_ctx.forest) {
+        s->maniac_ctx.forest = av_mallocz((s->num_planes) * sizeof(*(s->maniac_ctx.forest)));
+        if (!s->maniac_ctx.forest) {
+            return AVERROR(ENOMEM);
+        }
+        s->segment = s->i = 0;
+    }
+
+    switch (s->segment) {
+        for (;s->i < s->num_planes; s->i++) {
+    case 0:
+            if (!(s->ia % 2))
+                ff_flif16_maniac_prop_ranges_init(s->prop_ranges, &s->prop_ranges_size, s->range,
+                                                  s->i, s->num_planes);
+            else
+                ff_flif16_maniac_ni_prop_ranges_init(s->prop_ranges, &s->prop_ranges_size, s->range,
+                                                     s->i, s->num_planes);
+            s->segment++;
+
+    case 1:
+            if (IS_CONSTANT(s->range, s->i)) {
+                s->segment--;
+                continue;
+            }
+            ret = ff_flif16_read_maniac_tree(&s->rc, &s->maniac_ctx, s->prop_ranges,
+                                             s->prop_ranges_size, s->i);
+            if (ret)
+                goto error;
+            s->segment--;
+        }
+    }
+
+    s->state = FLIF16_PIXELDATA;
+    s->segment = 0;
+    return 0;
+
+    error:
+    return ret;
+}
+
+/* ============================================================================
+ * Non interlaced plane decoding
+ * ============================================================================
+ */
+
+
+static inline FLIF16ColorVal flif16_ni_predict_calcprops(FLIF16DecoderContext *s,
+                                                         uint32_t fr, uint8_t p,
+                                                         uint32_t r,
+                                                         FLIF16ColorVal fallback,
+                                                         uint8_t nobordercases)
+{
+    FLIF16ColorVal guess, left, top, topleft, gradientTL;
+    int which = 0;
+    int index = 0;
+
+    if (p < 3) {
+        for (int pp = 0; pp < p; pp++) {
+            s->properties[index++] = PIXEL_GET(s, fr, pp, r, s->c);
+        }
+        if (s->range->num_planes > 3) {
+            s->properties[index++] = PIXEL_GET(s, fr, 3, r, s->c);
+        }
+    }
+
+    left = (nobordercases || s->c > 0 ? PIXEL_GET(s, fr, p, r, s->c - 1) :
+           (r > 0 ? PIXEL_GET(s, fr, p, r - 1, s->c) : fallback));
+    top = (nobordercases || r > 0 ? PIXEL_GET(s, fr, p, r - 1, s->c) : left);
+    topleft = (nobordercases || (r > 0 && s->c > 0) ? PIXEL_GET(s, fr, p, r - 1, s->c - 1) : (r > 0 ? top : left));
+    gradientTL = left + top - topleft;
+    guess = MEDIAN3(gradientTL, left, top);
+    ff_flif16_ranges_snap(s->range, p, s->properties, &s->min, &s->max, &guess);
+
+    if (guess == gradientTL)
+        which = 0;
+    else if (guess == left)
+        which = 1;
+    else if (guess == top)
+        which = 2;
+
+    s->properties[index++] = guess;
+    s->properties[index++] = which;
+
+    if (nobordercases || (s->c > 0 && r > 0)) {
+        s->properties[index++] = left - topleft;
+        s->properties[index++] = topleft - top;
+    } else {
+        s->properties[index++] = 0;
+        s->properties[index++] = 0;
+    }
+
+    if (nobordercases || (s->c + 1 < s->width && r > 0)) {
+        s->properties[index++] = top - PIXEL_GET(s, fr, p, r - 1, s->c + 1);
+    } else {
+        s->properties[index++] = 0;
+    }
+
+    if (nobordercases || r > 1) {
+        s->properties[index++] = PIXEL_GET(s, fr, p, r - 2, s->c) - top;
+    } else {
+        s->properties[index++] = 0;
+    }
+
+    if (nobordercases || s->c > 1) {
+        s->properties[index++] = PIXEL_GET(s, fr, p, r, s->c - 2) - left;
+    } else {
+        s->properties[index++] = 0;
+    }
+
+    return guess;
+}
+
+static inline FLIF16ColorVal flif16_ni_predict(FLIF16DecoderContext *s,
+                                               uint32_t fr, uint32_t p,
+                                               uint32_t r)
+{
+    uint32_t gray = s->grays[p];
+    FLIF16ColorVal left = (s->c > 0 ? PIXEL_GET(s, fr, p, r, s->c - 1) :
+                          (r > 0 ? PIXEL_GET(s, fr, p, r - 1, s->c) : gray));
+    FLIF16ColorVal top = (r > 0 ? PIXEL_GET(s, fr, p, r - 1, s->c) : left);
+    FLIF16ColorVal topleft = (r > 0 && s->c > 0 ? PIXEL_GET(s, fr, p, r - 1, s->c - 1) : top);
+    FLIF16ColorVal gradientTL = left + top - topleft;
+    return MEDIAN3(gradientTL, left, top);
+}
+
+static int flif16_read_ni_plane_row(FLIF16DecoderContext *s, uint8_t p, uint32_t fr,
+                                    uint32_t r)
+{
+    FLIF16ColorVal curr;
+    FLIF16ColorVal min_p = ff_flif16_ranges_min(s->range, p);
+
+    switch (s->segment2) {
+    case 0:
+        if (s->frames[fr].seen_before >= 0) {
+            return 0;
+        }
+
+        // If this is not the first or only frame, fill the beginning of the row
+        // before the actual pixel data
+        if (fr > 0) {
+            // If alphazero is on, fill with a predicted value, otherwise
+            // copy pixels from the previous frame
+
+            s->begin = (!s->frameshape) ? 0 : s->frames[fr].col_begin[r];
+            s->end   = (!s->frameshape) ? s->width : s->frames[fr].col_end[r];
+            if (s->alphazero && p < 3) {
+                for (uint32_t c = 0; c < s->begin; c++)
+                    if (PIXEL_GET(s, fr, 3, r, c) == 0) {
+                        PIXEL_SET(s, fr, p, r, c, flif16_ni_predict(s, fr, p, r));
+                    } else {
+                        PIXEL_SET(s, fr, p, r, c, PIXEL_GET(s, PREV_FRAMENUM(s->frames, fr), p, r, c));
+                    }
+            } else if (p != 4) {
+                ff_flif16_copy_cols(CTX_CAST(s), &s->frames[fr],
+                                    PREV_FRAME(s->frames, fr), p, r, 0, s->begin);
+            }
+        } else {
+            s->begin = 0;
+            s->end   = s->width;
+        }
+        s->segment2++;
+
+        if (r > 1 && !s->framelookback && s->begin == 0 && s->end > 3) {
+            // Decode actual pixel data
+            s->c = s->begin;
+
+            for (; s->c < 2; s->c++) {
+                if (s->alphazero && p < 3 &&
+                    PIXEL_GET(s, fr, 3, r, s->c) == 0) {
+                    PIXEL_SET(s, fr, p, r, s->c, flif16_ni_predict(s, fr, p, r));
+                    continue;
+                }
+                s->guess = flif16_ni_predict_calcprops(s, fr, p, r, min_p, 0);
+    case 1:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                ff_flif16_pixel_set(CTX_CAST(s), &s->frames[fr], p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (; s->c < s->end - 1; s->c++) {
+                if (s->alphazero && p < 3 &&
+                    PIXEL_GET(s, fr, 3, r, s->c) == 0) {
+                    PIXEL_SET(s, fr, p, r, s->c, flif16_ni_predict(s, fr, p, r));
+                    continue;
+                }
+                s->guess = flif16_ni_predict_calcprops(s, fr, p, r, min_p, 1);
+    case 2:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SET(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (; s->c < s->end; s->c++) {
+                if (s->alphazero && p < 3 &&
+                    PIXEL_GET(s, fr, 3, r, s->c) == 0) {
+                    PIXEL_SET(s, fr, p, r, s->c, flif16_ni_predict(s, fr, p, r));
+                    continue;
+                }
+                s->guess = flif16_ni_predict_calcprops(s, fr, p, r, min_p, 0);
+    case 3:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SET(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+        } else {
+            s->segment2 = 4;
+            for (s->c = s->begin; s->c < s->end; s->c++) {
+                if (s->alphazero && p < 3 &&
+                    PIXEL_GET(s, fr, 3, r, s->c) == 0) {
+                    PIXEL_SET(s, fr, p, r, s->c, flif16_ni_predict(s, fr, p, r));
+                    continue;
+                }
+                if (s->framelookback && p < 4 &&
+                    PIXEL_GET(s, fr, FLIF16_PLANE_LOOKBACK, r, s->c) > 0) {
+                    PIXEL_SET(s, fr, p, r, s->c,
+                    PIXEL_GET(s, LOOKBACK_FRAMENUM(s, s->frames, fr, r, s->c), p, r, s->c));
+                    continue;
+                }
+                s->guess = flif16_ni_predict_calcprops(s, fr, p, r, min_p, 0);
+                if (s->framelookback && p == FLIF16_PLANE_LOOKBACK && s->max > fr)
+                    s->max = fr;
+    case 4:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SET(s, fr, p, r, s->c, curr);
+            }
+        } // End if
+
+        // If this is not the first or only frame, fill the end of the row after
+        // the actual pixel data
+        if (fr > 0) {
+            if (s->alphazero && p < 3) {
+                for (uint32_t c = s->end; c < s->width; c++)
+                    if (PIXEL_GET(s, fr, 3, r, s->c) == 0) {
+                        PIXEL_SET(s, fr, p, r, s->c, flif16_ni_predict(s, fr, p, r));
+                    } else {
+                        PIXEL_SET(s, fr, p, r, s->c, PIXEL_GET(s, PREV_FRAMENUM(s->frames, fr), p, r, s->c));
+                    }
+            } else if(p != 4) {
+                ff_flif16_copy_cols(CTX_CAST(s), &s->frames[fr],
+                PREV_FRAME(s->frames, fr), p, r, s->end, s->width);
+            }
+        }
+    }
+
+    s->segment2 = 0;
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static int flif16_read_ni_image(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    int ret;
+
+    switch (s->segment) {
+    case 0:
+        for (int p = 0; p < s->range->num_planes; p++)
+            s->grays[p] = (ff_flif16_ranges_min(s->range, p) + ff_flif16_ranges_max(s->range, p)) / 2;
+        s->i = s->i2 = s->i3 = 0;
+        if (   (s->range->num_planes > 3 && ff_flif16_ranges_max(s->range, 3) == 0)
+            || (s->range->num_planes > 3 && ff_flif16_ranges_min(s->range, 3) > 0))
+            s->alphazero = 0;
+
+        s->segment++;
+
+        for (; s->i < 5; s->i++) {
+            s->curr_plane = plane_ordering[s->i];
+
+            if (s->curr_plane >= s->num_planes)
+                continue;
+
+            if (IS_CONSTANT(s->range, s->curr_plane))
+                continue;
+
+            for (; s->i2 < s->height; s->i2++) {
+                for (; s->i3 < s->num_frames; s->i3++) {
+    case 1:
+                    ret = flif16_read_ni_plane_row(s, s->curr_plane, s->i3, s->i2);
+                    if (ret)
+                        goto error;
+                } // End for
+                s->i3 = 0;
+            } // End for
+            s->i2 = 0;
+        } // End for
+    } // End switch
+
+    s->state = FLIF16_OUTPUT;
+    return 0;
+
+    error:
+    return ret;
+}
+
+/*
+ * ============================================================================
+ * Interlaced plane decoding
+ * ============================================================================
+ *
+ * This is how the data is organized here:
+ * 1. uni_int: rough_zoomlevel
+ * 2. (repeat num_planes times) values of top left pixels of each channel
+ * 3. Rough Pixeldata max_zoomlevel to rough_zoomlevel + 1
+ *    For this case, the MANIAC forest is initialised with a single node per
+ *    channel. This is nused with the maniac integer reader.
+ * 4. Actual Encoded MANIAC trees
+ * 5. Rest of the pixeldata rough_zoomlevel to 0
+ */
+
+static inline FLIF16ColorVal flif16_predict_horiz(FLIF16DecoderContext *s,
+                                                  uint32_t fr, uint8_t z,
+                                                  uint8_t p, uint32_t r,
+                                                  uint32_t rows)
+{
+    FLIF16ColorVal top, bottom, avg, left, topleft, bottomleft;
+    if (p == FLIF16_PLANE_LOOKBACK)
+        return 0;
+    top    = PIXEL_GETZ(s, fr, p, z, r - 1, s->c);
+    bottom = (r + 1 < rows ? PIXEL_GETZ(s, fr, p, z, r + 1, s->c) : top);
+
+    switch (s->ipp) {
+    case 0:
+        avg = (top + bottom)>>1;
+        return avg;
+
+    case 1:
+        avg        = (top + bottom) >> 1;
+        left       = (s->c > 0 ? PIXEL_GETZ(s, fr, p, z, r, s->c - 1) : top);
+        topleft    = (s->c > 0 ? PIXEL_GETZ(s, fr, p, z, r - 1, s->c - 1) : top);
+        bottomleft = (s->c > 0 && r + 1 < rows ? PIXEL_GETZ(s, fr, p, z, r + 1, s->c - 1) : left);
+        return MEDIAN3(avg, (FLIF16ColorVal) (left + top - topleft), (FLIF16ColorVal) (left + bottom - bottomleft));
+
+    default:
+        left = (s->c > 0 ? PIXEL_GETZ(s, fr, p, z, r, s->c - 1) : top);
+        return MEDIAN3(top, bottom, left);
+    }
+}
+
+static inline FLIF16ColorVal flif16_predict_vert(FLIF16DecoderContext *s,
+                                                 uint32_t fr, uint8_t z,
+                                                 uint8_t p, uint32_t r,
+                                                 uint32_t cols)
+{
+    FLIF16ColorVal top, left, right, avg, topleft, topright;
+    if (p == FLIF16_PLANE_LOOKBACK)
+        return 0;
+    left  = PIXEL_GETZ(s, fr, p, z, r, s->c - 1);
+    right = (s->c + 1 < cols ? PIXEL_GETZ(s, fr, p, z, r, s->c + 1) : left);
+
+    switch (s->ipp) {
+    case 0:
+        avg = (left + right) >> 1;
+        return avg;
+
+    case 1:
+        avg      = (left + right) >> 1;
+        top      = (r > 0 ? PIXEL_GETZ(s, fr, p, z, r - 1, s->c) : left);
+        topleft  = (r > 0 ? PIXEL_GETZ(s, fr, p, z , r - 1, s->c - 1) : left);
+        topright = (r > 0 && s->c + 1 < cols ? PIXEL_GETZ(s, fr, p, z, r - 1, s->c + 1) : top);
+        return MEDIAN3(avg, (FLIF16ColorVal) (left + top - topleft), (FLIF16ColorVal) (right + top - topright));
+
+    default:
+        top = (r > 0 ? PIXEL_GETZ(s, fr, p, z, r - 1, s->c) : left);
+        return MEDIAN3(top, left, right);
+    }
+}
+
+static inline FLIF16ColorVal flif16_predict_calcprops(FLIF16DecoderContext *s,
+                                                      uint32_t fr, int8_t z,
+                                                      uint8_t p, uint32_t r,
+                                                      uint8_t horizontal,
+                                                      uint8_t nobordercases)
+{
+    FLIF16ColorVal guess, left, top, topleft, topright, bottomleft, bottom,
+                   avg, topleftgradient, median, bottomright, right;
+    const uint8_t bottompresent = r + 1 < ZOOM_HEIGHT(s->height, z);
+    const uint8_t rightpresent  = s->c + 1 < ZOOM_WIDTH(s->width, z);
+    int which = 0;
+    int index = 0;
+
+    if (p < 3) {
+        if (p > 0) {
+            s->properties[index++] = PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r, s->c);
+        }
+        if (p > 1)
+            s->properties[index++] = PIXEL_GETZ(s, fr, FLIF16_PLANE_CO, z, r, s->c);
+        if (s->num_planes > 3)
+            s->properties[index++] = PIXEL_GETZ(s, fr, FLIF16_PLANE_ALPHA, z, r, s->c);
+    }
+
+    if (horizontal) { // filling horizontal lines
+        top        = PIXEL_GETFAST(s, fr, p, r - 1, s->c);
+        left       =   (nobordercases || s->c > 0
+                     ? PIXEL_GETFAST(s, fr, p, r, s->c - 1)
+                     : top);
+        topleft    =   (nobordercases || s->c > 0
+                     ? PIXEL_GETFAST(s, fr, p, r - 1, s->c - 1)
+                     : top);
+        topright   =   (nobordercases || (rightpresent)
+                     ? PIXEL_GETFAST(s, fr, p, r - 1, s->c + 1)
+                     : top);
+        bottomleft =   (nobordercases || (bottompresent && s->c > 0)
+                     ? PIXEL_GETFAST(s, fr, p, r + 1, s->c - 1)
+                     : left);
+        bottom     =   (nobordercases || bottompresent
+                     ? PIXEL_GETFAST(s, fr, p, r + 1, s->c)
+                     : left);
+        avg             = (top + bottom) >> 1;
+        topleftgradient = left + top - topleft;
+        median          = MEDIAN3(avg, topleftgradient, (FLIF16ColorVal) (left + bottom - bottomleft));
+        which = 2;
+
+        if (median == avg)
+            which = 0;
+        else if (median == topleftgradient)
+            which = 1;
+        s->properties[index++] = which;
+
+        if (p == FLIF16_PLANE_CO || p == FLIF16_PLANE_CG) {
+            s->properties[index++] = PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r, s->c) -
+                                     ((PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r - 1, s->c) +
+                                     PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y,
+                                     (nobordercases || bottompresent ? r + 1 : r - 1), s->c)) >> 1);
+        }
+
+        switch (s->predictor) {
+        case 0:
+            guess = avg;
+            break;
+        case 1:
+            guess = median;
+            break;
+        default:
+            guess = MEDIAN3(top, bottom, left);
+            break;
+        }
+
+        ff_flif16_ranges_snap(s->range, p, s->properties, &s->min, &s->max, &guess);
+        s->properties[index++] = top - bottom;
+        s->properties[index++] = top - ((topleft + topright) >> 1);
+        s->properties[index++] = left - ((bottomleft + topleft) >> 1);
+        bottomright =    (nobordercases || (rightpresent && bottompresent)
+                       ? PIXEL_GETFAST(s, fr, p, r + 1, s->c + 1)
+                       : bottom);
+        s->properties[index++] = bottom - ((bottomleft + bottomright) >> 1);
+    } else { // filling vertical lines
+        left       = PIXEL_GETFAST(s, fr, p, r, s->c - 1);
+        top        =   (nobordercases || r > 0
+                     ? PIXEL_GETFAST(s, fr, p, r - 1, s->c)
+                     : left);
+        topleft    =   (nobordercases || r > 0
+                     ? PIXEL_GETFAST(s, fr, p, r - 1, s->c - 1)
+                     : left);
+        topright   =   (nobordercases || (r > 0 && rightpresent)
+                     ? PIXEL_GETFAST(s, fr, p, r - 1, s->c + 1)
+                     : top);
+        bottomleft =   (nobordercases || (bottompresent)
+                     ? PIXEL_GETFAST(s, fr, p, r + 1, s->c - 1)
+                     : left);
+        right      =   (nobordercases || rightpresent
+                     ? PIXEL_GETFAST(s, fr, p, r, s->c + 1)
+                     : top);
+        avg        = (left + right) >> 1;
+        topleftgradient = left + top - topleft;
+        median = MEDIAN3(avg, topleftgradient, (FLIF16ColorVal) (right + top - topright));
+        which = 2;
+
+        if (median == avg)
+            which = 0;
+        else if (median == topleftgradient)
+            which = 1;
+
+        s->properties[index++] = which;
+
+        if (p == FLIF16_PLANE_CO || p == FLIF16_PLANE_CG) {
+            s->properties[index++] = PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r, s->c) -
+                                     ((PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r, s->c - 1) +
+                                     PIXEL_GETFAST(s, fr, FLIF16_PLANE_Y, r,
+                                     (nobordercases || rightpresent ? s->c + 1 : s->c - 1))) >> 1);
+        }
+
+        switch (s->predictor) {
+        case 0:
+            guess = avg;
+            break;
+        case 1:
+            guess = median;
+            break;
+        default:
+            guess = MEDIAN3(top, left, right);
+            break;
+        }
+
+        ff_flif16_ranges_snap(s->range, p, s->properties, &s->min, &s->max, &guess);
+        s->properties[index++] = left - right;
+        s->properties[index++] = left - ((bottomleft + topleft) >> 1);
+        s->properties[index++] = top  - ((topleft + topright) >> 1);
+        bottomright =   (nobordercases || (rightpresent && bottompresent)
+                      ? PIXEL_GETFAST(s, fr, p, r + 1, s->c + 1)
+                      : right);
+        s->properties[index++] = right - ((bottomright + topright) >> 1);
+    }
+
+    s->properties[index++] = guess;
+
+    if (p != 2) {
+        if (nobordercases || r > 1)
+            s->properties[index++] = PIXEL_GETFAST(s, fr, p, r - 2, s->c) - top; // toptop - top
+        else
+            s->properties[index++] = 0;
+        if (nobordercases || s->c > 1)
+            s->properties[index++] = PIXEL_GETFAST(s, fr, p, r, s->c - 2) - left; // leftleft - left
+        else
+            s->properties[index++] = 0;
+    }
+
+    return guess;
+}
+
+static int flif_read_plane_zl_horiz(FLIF16DecoderContext *s,
+                                    uint8_t alpha_plane, int p,
+                                    int z, uint32_t fr, uint32_t r)
+{
+    FLIF16ColorVal curr;
+    const uint32_t cs = ZOOM_COLPIXELSIZE(z), rs = ZOOM_ROWPIXELSIZE(z);
+    const uint32_t zh = ZOOM_HEIGHT(s->height, z), zw = ZOOM_WIDTH(s->width, z);
+
+    switch (s->segment2) {
+    case 0:
+        if (s->frames[fr].seen_before >= 0) {
+            return 0;
+        }
+
+        if (fr > 0) {
+            s->begin = s->frames[fr].col_begin[r * rs] / cs;
+            s->end   = 1 + (s->frames[fr].col_end[r * rs] - 1) / cs;
+            if (s->alphazero && p < 3) {
+                for (s->c = 0; s->c < s->begin; s->c++)
+                    if (PIXEL_GETZ(s, fr, FLIF16_PLANE_ALPHA, z, r, s->c) == 0)
+                        PIXEL_SETZ(s, fr, p,  z, r, s->c,
+                                   flif16_predict_horiz(s, fr, z, p, r, zh));
+                    else
+                        PIXEL_SETZ(s, fr, p, z, r, s->c,
+                                   PIXEL_GETZ(s, fr - 1, p, z, r, s->c));
+            } else if (p != 4) {
+                ff_flif16_copy_cols_stride(CTX_CAST(s), &s->frames[fr],
+                                           &s->frames[fr - 1], p,
+                                           rs * r, cs * 0, cs * s->begin, cs);
+                ff_flif16_copy_cols_stride(CTX_CAST(s), &s->frames[fr],
+                                           &s->frames[fr - 1], p,
+                                           rs * r, cs * s->end,
+                                           cs * zw, cs);
+            }
+        } else {
+            s->begin = 0;
+            s->end   = zw;
+        }
+
+        s->segment2++;
+
+        if (r > 1 && r < zh - 1 && !s->framelookback && s->begin == 0 && s->end > 3) {
+            for (s->c = s->begin; s->c < 2; s->c++) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_horiz(s, fr, z, p, r, zh));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 1, 0);
+    case 1:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (s->c = 2; s->c < s->end - 2; s->c++) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_horiz(s, fr, z, p, r, zh));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 1, 1);
+    case 2:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (s->c = s->end - 2; s->c < s->end; s->c++) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_horiz(s, fr, z, p, r, zh));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 1, 0);
+    case 3:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+        } else {
+            s->segment2 = 4;
+            for (s->c = s->begin; s->c < s->end; s->c++) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_horiz(s, fr, z, p, r, zh));
+                    continue;
+                }
+                if (s->framelookback && p < 4 && PIXEL_GETZ(s, fr, FLIF16_PLANE_LOOKBACK, z, r, s->c) > 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  PIXEL_GETZ(s, LOOKBACK_FRAMENUMZ(s, s->frames, fr, z, r, s->c),
+                                  p, z, r, s->c));
+                    continue;
+                }
+
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 1, 0);
+
+                if (s->framelookback && p == FLIF16_PLANE_LOOKBACK && s->max > fr)
+                    s->max = fr;
+                if (s->framelookback && (s->guess > s->max || s->guess < s->min))
+                    s->guess = s->min;
+    case 4:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+        }
+
+        if (fr>0 && s->alphazero && p < 3) {
+            for (uint32_t c = s->end; c < zw; c++)
+                if (PIXEL_GETZ(s, fr, p, z, r, s->c) == 0)
+                    PIXEL_SETZ(s, fr, p, z, r, s->c,
+                               flif16_predict_horiz(s, fr, z, p, r, zh));
+                else
+                    PIXEL_SETZ(s, fr, p, z, r, s->c, PIXEL_GETZ(s, fr - 1, p, z, r, s->c));
+        }
+    }
+
+    s->segment2 = 0;
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+}
+
+static int flif16_read_plane_zl_vert(FLIF16DecoderContext *s,
+                                     uint8_t alpha_plane, int p,
+                                     int z, uint32_t fr, uint32_t r)
+{
+    FLIF16ColorVal curr;
+
+    const uint32_t cs = ZOOM_COLPIXELSIZE(z), rs = ZOOM_ROWPIXELSIZE(z);
+    const uint32_t zh = ZOOM_HEIGHT(s->height, z), zw = ZOOM_WIDTH(s->width, z);
+
+    switch (s->segment2) {
+    case 0:
+        if (s->frames[fr].seen_before >= 0) {
+            return 0;
+        }
+        if (fr > 0) {
+            s->begin = (s->frames[fr].col_begin[r * rs] / cs);
+            s->end = (1 + (s->frames[fr].col_end[r * rs] - 1) / cs) | 1;
+            if (s->begin > 1 && ((s->begin & 1) == 0))
+                s->begin--;
+            if (s->begin == 0)
+                s->begin = 1;
+            if (s->alphazero && p < 3) {
+                for (s->c = 1; s->c < s->begin; s->c += 2)
+                    if (PIXEL_GETZ(s, fr, alpha_plane, z, r, s->c) == 0)
+                        PIXEL_SETZ(s, fr, p, z, r, s->c,
+                                   flif16_predict_vert(s, fr, z, p, r, zw));
+                    else
+                        PIXEL_SETZ(s, fr, p, z, r, s->c, PIXEL_GETZ(s, fr - 1, p, z, r, s->c));
+            } else if (p != 4) {
+                ff_flif16_copy_cols_stride(CTX_CAST(s), &s->frames[fr],
+                                           &s->frames[fr - 1], p,
+                                           rs * r, cs * 1, cs * s->begin, cs * 2);
+                ff_flif16_copy_cols_stride(CTX_CAST(s), &s->frames[fr],
+                                           &s->frames[fr - 1], p,
+                                           rs * r, cs * s->end, cs * zw, cs * 2);
+            }
+        } else {
+            s->begin = 1;
+            s->end = zw;
+        }
+        s->segment2++;
+
+        if (r > 1 && r < zh - 1 && !s->framelookback
+            && s->end == zw && s->end > 5 && s->begin == 1) {
+            s->c = s->begin;
+            for (; s->c < 3; s->c += 2) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_vert(s, fr, z, p, r, zw));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 0, 0);
+    case 1:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (; s->c < s->end - 2; s->c += 2) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_vert(s, fr, z, p, r, zw));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 0, 1);
+    case 2:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+            s->segment2++;
+
+            for (; s->c < s->end; s->c += 2) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_vert(s, fr, z, p, r, zw));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 0, 0);
+    case 3:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+        } else {
+            s->segment2 = 4;
+            for (s->c = s->begin; s->c < s->end; s->c += 2) {
+                if (s->alphazero && p < 3 && PIXEL_GETFAST(s, fr, alpha_plane, r, s->c) == 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                  flif16_predict_vert(s, fr, z, p, r, zw));
+                    continue;
+                }
+                if (s->framelookback && p < 4
+                     && PIXEL_GETZ(s, fr, FLIF16_PLANE_LOOKBACK, z, r, s->c) > 0) {
+                    PIXEL_SETFAST(s, fr, p, r, s->c,
+                                 PIXEL_GETZ(s, LOOKBACK_FRAMENUMZ(s, s->frames,
+                                 fr, z, r, s->c), p, z, r, s->c));
+                    continue;
+                }
+                s->guess = flif16_predict_calcprops(s, fr, z, p, r, 0, 0);
+                if (s->framelookback && p == FLIF16_PLANE_LOOKBACK && s->max > fr)
+                    s->max = fr;
+                if (s->framelookback && (s->guess > s->max || s->guess < s->min))
+                    s->guess = s->min;
+    case 4:
+                MANIAC_GET(&s->rc, &s->maniac_ctx, s->properties, p,
+                           s->min - s->guess, s->max - s->guess, &curr);
+                curr += s->guess;
+                PIXEL_SETFAST(s, fr, p, r, s->c, curr);
+            }
+        }
+    }
+
+    if (fr > 0 && s->alphazero && p < 3) {
+        for (s->c = s->end; s->c < zw; s->c += 2)
+            if (PIXEL_GETZ(s, fr - 1, alpha_plane, z, r, s->c) == 0)
+                PIXEL_SETZ(s, fr, p, z, r, s->c,
+                           flif16_predict_vert(s, fr, z, p, r, zw));
+            else
+                PIXEL_SETZ(s, fr, p, z, r, s->c, PIXEL_GETZ(s, fr - 1, p, z, r, s->c));
+    }
+
+
+    s->segment2 = 0;
+    return 0;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+
+}
+
+static inline int plane_zoomlevels(uint8_t num_planes, int begin_zl, int end_zl)
+{
+    return num_planes * (begin_zl - end_zl + 1);
+}
+
+static inline int get_plane_zoomlevel(uint8_t num_planes, int begin_zl, int end_zl,
+                                      int i, FLIF16RangesContext *ranges)
+{
+    int zl_list[MAX_PLANES] = {0};
+    int nextp, highest_priority_plane = 0;
+
+    /*
+     * From reference decoder:
+     *
+     * more advanced order: give priority to more important plane(s)
+     * assumption: plane 0 is luma, plane 1 is chroma, plane 2 is less important
+     * chroma, plane 3 is perhaps alpha, plane 4 are frame lookbacks (lookback
+     * transform, animation only)
+     */
+
+    int max_behind[] = {0, 2, 4, 0, 0};
+
+    if (IS_CONSTANT(ranges, 0)) {
+        max_behind[1] = 0;
+        max_behind[2] = 1;
+    }
+
+    for (int i = 0; i < num_planes; i++)
+        zl_list[i] = begin_zl + 1;
+
+    if (num_planes >= 5)
+        highest_priority_plane = 4; // lookbacks first
+    else if (num_planes >= 4)
+        highest_priority_plane = 3; // alpha first
+
+    nextp = highest_priority_plane;
+
+    while (i >= 0) {
+        zl_list[nextp]--;
+        i--;
+        if (i < 0)
+            break;
+        nextp = highest_priority_plane;
+        for (int p = 0; p < num_planes; p++) {
+            if (zl_list[p] > zl_list[highest_priority_plane] + max_behind[p]) {
+                nextp = p;
+            }
+        }
+
+        // ensure that nextp is not at the most detailed zoomlevel yet
+        while (zl_list[nextp] <= end_zl)
+            nextp = (nextp + 1) % num_planes;
+    }
+
+    return nextp;
+}
+
+static int flif16_read_image(AVCodecContext *avctx, uint8_t rough) {
+    FLIF16DecoderContext *s = avctx->priv_data;
+    int ret;
+    int temp;
+    uint8_t nump = s->num_planes;
+    uint8_t alpha_plane = (s->num_planes > 3) ? 3 : 0;
+
+    if (!rough && !s->segment) { // Are we decoding the main pixeldata segment?
+        s->begin_zl = s->rough_zl;
+        s->end_zl = 0;
+        s->segment = 5;
+    }
+
+    switch (s->segment) {
+    case 0:
+        flif16_blank_maniac_forest_init(avctx);
+        s->segment++;
+
+    case 1:
+        s->begin_zl = 0;
+        while (   ZOOM_ROWPIXELSIZE(s->begin_zl) < s->height
+               || ZOOM_COLPIXELSIZE(s->begin_zl) < s->width)
+            s->begin_zl++;
+        s->segment++;
+
+    case 2:
+        RAC_GET(&s->rc, NULL, 0, s->begin_zl, &s->rough_zl, FLIF16_RAC_UNI_INT32);
+        s->end_zl = s->rough_zl + 1;
+        s->segment++;
+
+        // Read top left pixels of all planes
+        s->i = 0;
+        s->i2 = 0;
+        for (; s->i < s->num_planes; s->i++) {
+            if (!IS_CONSTANT(s->range, s->i)) {
+                for (; s->i2 < s->num_frames; s->i2++) {
+    case 3:
+                    RAC_GET(&s->rc, NULL, ff_flif16_ranges_min(s->range, s->i),
+                            ff_flif16_ranges_max(s->range, s->i) - ff_flif16_ranges_min(s->range, s->i),
+                            &temp, FLIF16_RAC_UNI_INT32);
+                    PIXEL_SETZ(s, s->i2, s->i, 0, 0, 0, temp);
+                }
+                s->i2 = 0;
+            }
+        }
+        s->segment++;
+
+    case 4:
+        for (int i = 0; i < nump; i++)
+            s->zoomlevels[i] = s->begin_zl;
+        s->segment++;
+
+    /* Inner Segment */
+    case 5:
+        RAC_GET(&s->rc, NULL, 0, 1, &s->default_order, FLIF16_RAC_UNI_INT8);
+        s->segment++;
+
+        for (s->i = 0; s->i < nump; s->i++) {
+    case 6:
+            RAC_GET(&s->rc, NULL, -1, MAX_PREDICTORS + 1, &s->predictors[s->i], FLIF16_RAC_UNI_INT32);
+        }
+        s->segment++;
+
+        for (s->i = 0; s->i < plane_zoomlevels(nump, s->begin_zl, s->end_zl); s->i++) {
+    case 7:
+            if (s->default_order) {
+                s->curr_plane = get_plane_zoomlevel(s->num_planes, s->begin_zl,
+                                                    s->end_zl, s->i, s->range);
+            } else {
+                RAC_GET(&s->rc, NULL, 0, nump - 1, &s->curr_plane, FLIF16_RAC_UNI_INT32);
+            }
+            s->segment++;
+            s->curr_zoom = s->zoomlevels[s->curr_plane];
+
+            if (s->curr_zoom < 0) {
+                av_log(s, AV_LOG_ERROR, "invalid plane/zoomlevel\n");
+                return AVERROR_INVALIDDATA;
+            }
+
+            if (!IS_CONSTANT(s->range, s->curr_plane)) {
+                if (s->predictors[s->curr_plane] < 0) {
+    case 8:
+                    RAC_GET(&s->rc, NULL, 0, MAX_PREDICTORS, &s->predictor, FLIF16_RAC_UNI_INT32);
+                } else {
+                    s->predictor = s->predictors[s->curr_plane];
+                }
+                s->segment++;
+
+                for(int fr = 0; fr < s->num_frames; fr++) {
+                    ff_flif16_prepare_zoomlevel(CTX_CAST(s), &s->frames[fr],
+                                                s->curr_plane, s->curr_zoom);
+                    if (s->curr_plane > 0)
+                        ff_flif16_prepare_zoomlevel(CTX_CAST(s), &s->frames[fr],
+                                                    0, s->curr_zoom);
+                    if (s->curr_plane < 3 && s->num_planes > 3)
+                        ff_flif16_prepare_zoomlevel(CTX_CAST(s), &s->frames[fr],
+                                                    3, s->curr_zoom);
+                }
+
+                if (!(s->curr_zoom % 2)) {
+                    s->segment = 9;
+                    for (s->i2 = 1; s->i2 < ZOOM_HEIGHT(s->height, s->curr_zoom); s->i2 += 2) {
+                        for (s->i3 = 0; s->i3 < s->num_frames; s->i3++) {
+    case 9:
+                            if(ret = flif_read_plane_zl_horiz(s, alpha_plane,
+                               s->curr_plane, s->curr_zoom, s->i3, s->i2))
+                                goto error;
+                        }
+                    }
+                } else {
+                    s->segment = 10;
+                    for (s->i2 = 0; s->i2 < ZOOM_HEIGHT(s->height, s->curr_zoom); s->i2++) {
+                        for (s->i3 = 0; s->i3 < s->num_frames; s->i3++) {
+    case 10:
+                            if(ret = flif16_read_plane_zl_vert(s, alpha_plane,
+                               s->curr_plane, s->curr_zoom, s->i3, s->i2))
+                                goto error;
+                        }
+                    }
+                }
+
+                s->zoomlevels[s->curr_plane]--;
+            } else
+                s->zoomlevels[s->curr_plane]--;
+            s->segment = 7;
+        } // End For
+    } // End Switch
+
+    s->state = FLIF16_OUTPUT;
+    s->segment = 0;
+    s->segment2 = 0;
+    return ret;
+
+    need_more_data:
+    return AVERROR(EAGAIN);
+
+    error:
+    return ret;
+}
+
+static int flif16_read_pixeldata(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    int ret;
+    if((s->ia % 2))
+        ret = flif16_read_ni_image(avctx);
+    else {
+        ret = flif16_read_image(avctx, (s->state == FLIF16_ROUGH_PIXELDATA));
+    }
+
+    if(!ret)
+        s->state = FLIF16_OUTPUT;
+
+    return ret;
+}
+
+static int flif16_write_frame(AVCodecContext *avctx, AVFrame *data)
+{
+    uint32_t target_frame;
+    int ret;
+    FLIF16DecoderContext *s = avctx->priv_data;
+    s->out_frame->pict_type = AV_PICTURE_TYPE_I;
+
+    if ((ret = ff_set_dimensions(avctx, s->width, s->height)) < 0)
+        return ret;
+
+    if (s->bpc > 65535) {
+        av_log(avctx, AV_LOG_ERROR, "depth per channel greater than 16 bits not supported\n");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    avctx->pix_fmt = flif16_out_frame_type[FFMIN(s->num_planes, 4)][s->bpc > 255];
+
+    if ((ret = ff_reget_buffer(avctx, s->out_frame, 0)) < 0) {
+        return ret;
+    }
+
+    target_frame = (s->frames[s->out_frames_count].seen_before >= 0)
+                   ? s->frames[s->out_frames_count].seen_before
+                   : s->out_frames_count;
+
+    if (s->num_frames > 1) {
+        s->out_frame->pts = s->pts;
+        s->pts += s->framedelay[s->out_frames_count];
+    }
+
+    // Clear out transparent pixels
+    if (s->num_planes > 3) {
+        for (uint32_t i = 0; i < s->height; i++)
+            for (uint32_t j = 0; j < s->width; j++)
+                if (!PIXEL_GET(s, s->out_frames_count, FLIF16_PLANE_ALPHA, i, j)) {
+                    PIXEL_SET(s, s->out_frames_count, FLIF16_PLANE_Y, i, j, 0);
+                    PIXEL_SET(s, s->out_frames_count, FLIF16_PLANE_CO, i, j, 0);
+                    PIXEL_SET(s, s->out_frames_count, FLIF16_PLANE_CG, i, j, 0);
+                }
+    }
+
+    switch (avctx->pix_fmt) {
+    case AV_PIX_FMT_GRAY8:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *(s->out_frame->data[0] + i * s->out_frame->linesize[0] + j) = \
+                PIXEL_GET(s, target_frame, 0, i, j);
+            }
+        }
+        break;
+
+    case AV_PIX_FMT_RGB24:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *(s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 3 + 0 ) = \
+                PIXEL_GET(s, target_frame, 0, i, j);
+                *(s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 3 + 1) = \
+                PIXEL_GET(s, target_frame, 1, i, j);
+                *(s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 3 + 2) = \
+                PIXEL_GET(s, target_frame, 2, i, j);
+            }
+        }
+        break;
+
+    case AV_PIX_FMT_RGB32:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *((uint32_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 4))
+                = (PIXEL_GET(s, target_frame, 3, i, j) << 24) |
+                  (PIXEL_GET(s, target_frame, 0, i, j) << 16) |
+                  (PIXEL_GET(s, target_frame, 1, i, j) << 8)  |
+                   PIXEL_GET(s, target_frame, 2, i, j);
+            }
+        }
+        break;
+
+    case AV_PIX_FMT_GRAY16:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *((uint16_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 2)) = \
+                PIXEL_GET(s, target_frame, 0, i, j);
+            }
+        }
+        break;
+
+    case AV_PIX_FMT_RGB48:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *((uint16_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 6 + 0)) = \
+                PIXEL_GET(s, target_frame, 0, i, j);
+                *((uint16_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 6 + 1)) = \
+                PIXEL_GET(s, target_frame, 1, i, j);
+                *((uint16_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 6 + 2)) = \
+                PIXEL_GET(s, target_frame, 2, i, j);
+            }
+        }
+
+    case AV_PIX_FMT_RGBA64:
+        for (uint32_t i = 0; i < s->height; i++) {
+            for (uint32_t j = 0; j < s->width; j++) {
+                *((uint64_t *) (s->out_frame->data[0] + i * s->out_frame->linesize[0] + j * 8))
+                = (((uint64_t) PIXEL_GET(s, target_frame, 3, i, j)) << 48) |
+                  (((uint64_t) PIXEL_GET(s, target_frame, 2, i, j)) << 32) |
+                  (((uint64_t) PIXEL_GET(s, target_frame, 1, i, j)) << 16) |
+                   ((uint64_t) PIXEL_GET(s, target_frame, 0, i, j));
+            }
+        }
+        break;
+
+    default:
+        av_log(avctx, AV_LOG_FATAL, "Pixel format %d out of bounds?\n", avctx->pix_fmt);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    av_frame_ref(data, s->out_frame);
+    if ((++s->out_frames_count) >= s->num_frames)
+        s->state = FLIF16_EOS;
+
+    return 0;
+}
+
+static int flif16_decode_init(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    s->out_frame = av_frame_alloc();
+    if (!s->out_frame)
+        return AVERROR(ENOMEM);
+    return 0;
+}
+
+static int flif16_decode_frame(AVCodecContext *avctx,
+                               void *data, int *got_frame,
+                               AVPacket *avpkt)
+{
+    int ret = 0;
+    FLIF16DecoderContext *s = avctx->priv_data;
+    const uint8_t *buf      = avpkt->data;
+    int buf_size            = avpkt->size;
+    AVFrame *p              = data;
+
+    bytestream2_init(&s->gb, buf, buf_size);
+
+    /*
+     * Looping is done to change states in between functions.
+     * Function will either exit on AVERROR(EAGAIN) or AVERROR_EOF
+     */
+    do {
+        switch(s->state) {
+        case FLIF16_HEADER:
+            ret = flif16_read_header(avctx);
+            break;
+
+        case FLIF16_SECONDHEADER:
+            ret = flif16_read_second_header(avctx);
+            break;
+
+        case FLIF16_TRANSFORM:
+            ret = flif16_read_transforms(avctx);
+            break;
+
+        case FLIF16_ROUGH_PIXELDATA:
+            ret = flif16_read_pixeldata(avctx);
+            if (!ret) {
+                ff_flif16_maniac_close(&s->maniac_ctx, s->num_planes,
+                                       s->framelookback);
+                s->state = FLIF16_MANIAC;
+            }
+            break;
+
+        case FLIF16_MANIAC:
+            ret = flif16_read_maniac_forest(avctx);
+            break;
+
+        case FLIF16_PIXELDATA:
+            ret = flif16_read_pixeldata(avctx);
+            if (!ret) {
+                for (int i = 0; i < s->num_frames; i++) {
+                    if (s->frames[i].seen_before >= 0)
+                        continue;
+                    for (int j = s->transform_top - 1; j >= 0; j--) {
+                        ff_flif16_transform_reverse(CTX_CAST(s), s->transforms[j],
+                                                    &s->frames[i], 1, 1);
+                    }
+                }
+            }
+            break;
+
+        case FLIF16_OUTPUT:
+            ret = flif16_write_frame(avctx, p);
+            if (!ret) {
+                *got_frame = 1;
+                return buf_size;
+            }
+            break;
+
+        case FLIF16_EOS:
+            return AVERROR_EOF;
+        }
+
+    } while (!ret);
+
+    return ret;
+}
+
+static av_cold int flif16_decode_end(AVCodecContext *avctx)
+{
+    FLIF16DecoderContext *s = avctx->priv_data;
+    av_freep(&s->framedelay);
+    if (s->frames)
+        ff_flif16_frames_free(&s->frames, s->num_frames, s->num_planes, s->framelookback);
+
+    for (int i = s->transform_top - 1; i >= 0; i--)
+        ff_flif16_transforms_close(s->transforms[i]);
+
+    ff_flif16_maniac_close(&s->maniac_ctx, s->num_planes, s->framelookback);
+    av_frame_free(&s->out_frame);
+
+    if (s->range)
+        ff_flif16_ranges_close(s->range);
+    return 0;
+}
+
+AVCodec ff_flif16_decoder = {
+    .name           = "flif16",
+    .long_name      = NULL_IF_CONFIG_SMALL("FLIF (Free Lossless Image Format)"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_FLIF16,
+    .init           = flif16_decode_init,
+    .decode         = flif16_decode_frame,
+    .close          = flif16_decode_end,
+    .priv_data_size = sizeof(FLIF16DecoderContext),
+    .capabilities   = AV_CODEC_CAP_DELAY,
+    .priv_class     = NULL,
+};
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index 7d75cea830..e5956d81cd 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -40,6 +40,7 @@ extern AVCodecParser ff_dvbsub_parser;
 extern AVCodecParser ff_dvdsub_parser;
 extern AVCodecParser ff_dvd_nav_parser;
 extern AVCodecParser ff_flac_parser;
+extern AVCodecParser ff_flif16_parser;
 extern AVCodecParser ff_g723_1_parser;
 extern AVCodecParser ff_g729_parser;
 extern AVCodecParser ff_gif_parser;
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 5bdfdce363..739caf5c6e 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  58
-#define LIBAVCODEC_VERSION_MINOR 101
+#define LIBAVCODEC_VERSION_MINOR 102
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
-- 
2.28.0




More information about the ffmpeg-devel mailing list