diff mbox series

[FFmpeg-devel,v3,RFC] lavc, lavfmt: add FLIF decoding support

Message ID 20200811180821.22795-1-aghorui@teknik.io
State New
Headers show
Series [FFmpeg-devel,v3,RFC] lavc, lavfmt: add FLIF decoding support | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Anamitra Ghorui Aug. 11, 2020, 6:08 p.m. UTC
This patch adds support for non animated, animated, non interlaced and
interlaced FLIF images.
Hopefully, all previously mentioned mistakes have been resolved.
However, there are a few things I want to ask:

* The decoder can accept arbitary packet sizes. How do I make the
  demuxer (specifically flif16_read_packet) produce a packet that is
  reasonably sized? Using a static packet size (such as 4096) produces
  errors
* FLIF can encode ICCP color profiles as metadata. These however, as
  per libavcodec/pngdec.c some processing is done per frame, along with
  setting the dictionary entry. How should this be handled in the case 
  of a non-still image situation?
  
Test files are available here: https://0x0.st/iYs_.zip

Thanks,
Anamitra

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

Signed-off-by: Anamitra Ghorui <aghorui@teknik.io>
---
 Changelog                      |    3 +-
 configure                      |    1 +
 doc/general.texi               |    2 +
 libavcodec/Makefile            |    2 +
 libavcodec/allcodecs.c         |    1 +
 libavcodec/codec_desc.c        |    7 +
 libavcodec/codec_id.h          |    1 +
 libavcodec/flif16.c            |  191 ++
 libavcodec/flif16.h            |  276 +++
 libavcodec/flif16_parser.c     |  193 ++
 libavcodec/flif16_rangecoder.c |  798 +++++++++
 libavcodec/flif16_rangecoder.h |  393 +++++
 libavcodec/flif16_transform.c  | 3009 ++++++++++++++++++++++++++++++++
 libavcodec/flif16_transform.h  |  121 ++
 libavcodec/flif16dec.c         | 1787 +++++++++++++++++++
 libavcodec/parsers.c           |    1 +
 libavcodec/version.h           |    2 +-
 libavformat/Makefile           |    1 +
 libavformat/allformats.c       |    1 +
 libavformat/flifdec.c          |  435 +++++
 libavformat/version.h          |    2 +-
 21 files changed, 7224 insertions(+), 3 deletions(-)
 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
 create mode 100644 libavformat/flifdec.c

Comments

Andreas Rheinhardt Aug. 11, 2020, 6:42 p.m. UTC | #1
Anamitra Ghorui:
> This patch adds support for non animated, animated, non interlaced and
> interlaced FLIF images.
> Hopefully, all previously mentioned mistakes have been resolved.
> However, there are a few things I want to ask:
> 
> * The decoder can accept arbitary packet sizes. How do I make the
>   demuxer (specifically flif16_read_packet) produce a packet that is
>   reasonably sized? Using a static packet size (such as 4096) produces
>   errors
> * FLIF can encode ICCP color profiles as metadata. These however, as
>   per libavcodec/pngdec.c some processing is done per frame, along with
>   setting the dictionary entry. How should this be handled in the case 
>   of a non-still image situation?
>   
> Test files are available here: https://0x0.st/iYs_.zip
> 
> Thanks,
> Anamitra
> 
> Co-authored-by: Anamitra Ghorui <aghorui@teknik.io>
> Co-authored-by: Kartik K Khullar <kartikkhullar840@gmail.com>
> 
> Signed-off-by: Anamitra Ghorui <aghorui@teknik.io>
> ---

[...]

> diff --git a/libavformat/flifdec.c b/libavformat/flifdec.c
> new file mode 100644
> index 0000000000..8fbedd6b58
> --- /dev/null
> +++ b/libavformat/flifdec.c
> @@ -0,0 +1,435 @@
> +/*
> + * FLIF16 demuxer
> + * Copyright (c) 2020 Anamitra Ghorui <aghorui@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
> + * FLIF demuxer.
> + */
> +
> +#include "avformat.h"
> +#include "libavutil/common.h"
> +#include "libavutil/bprint.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +#include "internal.h"
> +#include "libavcodec/exif.h"
> +
> +#include "libavcodec/flif16.h"
> +#include "libavcodec/flif16_rangecoder.h"
> +
> +#include "config.h"
> +
> +#if CONFIG_ZLIB
> +#include <zlib.h>
> +#endif
> +
> +/*
> + * FLIF's reference encoder currently encodes metadata as a raw DEFLATE stream
> + * (RFC 1951). In order to decode a raw deflate stream using Zlib, inflateInit2
> + * must be used with windowBits being between -8 .. -15.
> + */
> +#define ZLIB_WINDOW_BITS -15
> +#define BUF_SIZE 4096
> +
> +typedef struct FLIFDemuxContext {
> +    const AVClass *class;
> +#if CONFIG_ZLIB
> +    z_stream stream;
> +    uint8_t active;
> +#endif
> +} FLIFDemuxContext;
> +
> +

[..]

> +
> +
> +static const AVOption options[] = {
> +    { NULL }
> +};

You don't have options, so you don't need options or an AVClass. The
const AVClass * in your context should then also be removed.

> +
> +static const AVClass demuxer_class = {
> +    .class_name = "FLIF demuxer",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +    .category   = AV_CLASS_CATEGORY_DEMUXER,
> +};
> +
> +AVInputFormat ff_flif_demuxer = {
> +    .name           = "flif",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Free Lossless Image Format (FLIF)"),
> +    .priv_data_size = sizeof(FLIFDemuxContext),
> +    .extensions     = "flif",
> +    .read_probe     = flif16_probe,
> +    .read_header    = flif16_read_header,
> +    .read_packet    = flif16_read_packet,
> +    .priv_class     = &demuxer_class,
> +};

I haven't looked at it closely at all, yet we typically want decoders
and demuxers to be in separate patches.

- Andreas
Anamitra Ghorui Aug. 12, 2020, 12:40 p.m. UTC | #2
On Tue, 11 Aug 2020 23:38:21 +0530
Anamitra Ghorui <aghorui@teknik.io> wrote:

> This patch adds support for non animated, animated, non interlaced and
> interlaced FLIF images.
> Hopefully, all previously mentioned mistakes have been resolved.
> However, there are a few things I want to ask:
> 
> * The decoder can accept arbitary packet sizes. How do I make the
>   demuxer (specifically flif16_read_packet) produce a packet that is
>   reasonably sized? Using a static packet size (such as 4096) produces
>   errors
> * FLIF can encode ICCP color profiles as metadata. These however, as
>   per libavcodec/pngdec.c some processing is done per frame, along
>   with setting the dictionary entry. How should this be handled in the
>   case of a non-still image situation?
>   
> Test files are available here: https://0x0.st/iYs_.zip
> 
> Thanks,
> Anamitra

[...]

A few things that I have noticed since uploading this patch:

* There are places where preincrements have not been converted to 
  postincrements. I had not noticed these and will add these in to the
  next patch.
* In the initialisation of planes, av_mallocz_array has been used for
  the fill mode. This is redundant and I will remove it.
* Using GetByteContext in the RangeCoder struct is not very useful if
  one is writing an encoder as well. Keeping pointers to the start,
  current position and the end of the buffer is more sound.
* In ff_flif16_copy_cols, instead of incrementing pointers, a for loop
  and offset calculation is used. I had misunderstood the initial intent
  of the review of this part.

A few more things I want to mention that I did not in the initial post:
* Kartik has said that the ColorBuckets algorithm and datastructure have
  been improved
* In PermutePlanes, we haven't yet observed whether constant planes are
  permuted around or not (I do think that since it will only ever be 
  the alpha plane that will be a true constant plane in case multiple
  planes exist, and it being an alpha plane most likely means that it
  wouldn't be permuted with others), so we haven't used pointer
  iteration for copying around plane data.


On Tue, 11 Aug 2020 20:42:02 +0200
Andreas Rheinhardt <andreas.rheinhardt@gmail.com> wrote:

[...]

> > +
> > +
> > +static const AVOption options[] = {
> > +    { NULL }
> > +};  
> 
> You don't have options, so you don't need options or an AVClass. The
> const AVClass * in your context should then also be removed.
> 

I see. Will remove.

[...]

> 
> I haven't looked at it closely at all, yet we typically want decoders
> and demuxers to be in separate patches.
> 
> - Andreas

Will do on the next patchset.

Thanks,
Anamitra
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 0108f8f1a8..d307029a40 100644
--- a/Changelog
+++ b/Changelog
@@ -13,7 +13,8 @@  version <next>:
 - Cineform HD encoder
 - ADPCM Argonaut Games encoder
 - Argonaut Games ASF muxer
-
+- FLIF16 decoder
+- FLIF16 demuxer
 
 version 4.3:
 - v360 filter
diff --git a/configure b/configure
index 8de1afcb99..fdaa6f142c 100755
--- a/configure
+++ b/configure
@@ -3300,6 +3300,7 @@  eac3_demuxer_select="ac3_parser"
 f4v_muxer_select="mov_muxer"
 fifo_muxer_deps="threads"
 flac_demuxer_select="flac_parser"
+flif_demuxer_select="zlib exif"
 flv_muxer_select="aac_adtstoasc_bsf"
 gxf_muxer_select="pcm_rechunk_bsf"
 hds_muxer_select="flv_muxer"
diff --git a/doc/general.texi b/doc/general.texi
index feae180f1d..637cc59c55 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -904,6 +904,8 @@  following image formats are supported:
 @item Flash Screen Video v2  @tab  X  @tab  X
 @item Flash Video (FLV)      @tab  X  @tab  X
     @tab Sorenson H.263 used in Flash
+@item FLIF (Free Lossless Image Format @tab     @tab  X
+    @tab Precursor to JPEG XL and FUIF
 @item FM Screen Capture Codec  @tab     @tab  X
 @item Forward Uncompressed   @tab     @tab  X
 @item Fraps                  @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index fc4294816e..8bbb9f84f1 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -329,6 +329,7 @@  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
@@ -1071,6 +1072,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 4bd830e5d0..e9bea12494 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 ced00bd34c..4ca0d1f514 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..67a66e583b
--- /dev/null
+++ b/libavcodec/flif16.c
@@ -0,0 +1,191 @@ 
+/*
+ * FLIF16 Image Format Definitions
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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 *frames,
+                          uint8_t *plane_mode, uint8_t *const_plane_value,
+                          uint8_t lookback)
+{
+    for (int j = 0; j < s->num_frames; ++j) {
+        if (frames[j].seen_before >= 0)
+            continue;
+
+        /* Multiplication overflow is dealt with in the decoder/encoder. */
+        for (int i = 0; i < s->num_planes; ++i) {
+            switch (plane_mode[i]) {
+            case FLIF16_PLANEMODE_NORMAL:
+                frames[j].data[i] = av_malloc_array(s->width * s->height, sizeof(int32_t));
+                if (!frames[j].data[i])
+                    return AVERROR(ENOMEM);
+                break;
+
+            case FLIF16_PLANEMODE_CONSTANT:
+                frames[j].data[i] = av_malloc(sizeof(int32_t));
+                if (!frames[j].data[i])
+                    return AVERROR(ENOMEM);
+                ((int32_t *) frames[j].data[i])[0] = const_plane_value[i];
+                break;
+
+            case FLIF16_PLANEMODE_FILL:
+                frames[j].data[i] = av_mallocz_array(s->width * s->height, sizeof(int32_t));
+                if (!frames[j].data[i])
+                    return AVERROR(ENOMEM);
+                for (int k = 0; k < s->width * s->height; ++k)
+                        ((int32_t *) frames[j].data[i])[k] = const_plane_value[i];
+                break;
+            }
+        }
+    }
+
+    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(FLIF16Context *s)
+{
+    FLIF16PixelData *frames = av_mallocz_array(s->num_frames, sizeof(*frames));
+    if (!frames)
+        return NULL;
+
+    for (int i = 0; i < s->num_frames; ++i)
+        frames[i].seen_before = -1;
+    return 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..4737212d93
--- /dev/null
+++ b/libavcodec/flif16.h
@@ -0,0 +1,276 @@ 
+/*
+ * FLIF16 Image Format Definitions
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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];
+} FLIF16PixelData;
+
+typedef int32_t FLIF16ColorVal;
+
+typedef struct FLIF16Context {
+    FLIF16MANIACContext maniac_ctx;
+    FLIF16RangeCoder rc;
+    GetByteContext gb;
+
+    // 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
+    uint8_t  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, const int plane,
+                   FLIF16ColorVal *prev_planes, FLIF16ColorVal *minv,
+                   FLIF16ColorVal *maxv);
+    void (*snap)(FLIF16RangesContext*, const int, FLIF16ColorVal*,
+                 FLIF16ColorVal*, FLIF16ColorVal*, FLIF16ColorVal*);
+    uint8_t is_static;
+    void (*close)(FLIF16RangesContext*);
+} FLIF16Ranges;
+
+typedef struct FLIF16TransformContext {
+    uint8_t t_no;
+    unsigned int segment;     ///< Segment the code is executing in.
+    int i;                    ///< Variable to store iteration number.
+    uint8_t done;
+    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 (*forward) (FLIF16Context *ctx, FLIF16TransformContext *t_ctx, FLIF16PixelData *frame);
+    int (*reverse) (FLIF16Context *ctx, FLIF16TransformContext *t_ctx, FLIF16PixelData *frame,
+                    uint32_t stride_row, uint32_t stride_col);
+    void (*configure) (FLIF16TransformContext *, const int);
+    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 *frames,
+                          uint8_t *is_const, uint8_t *const_plane_value,
+                          uint8_t lookback);
+
+FLIF16PixelData *ff_flif16_frames_init(FLIF16Context *s);
+
+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)
+{
+    for (uint32_t col = col_start; col < col_end; ++col) {
+        ((FLIF16ColorVal *) dest->data[plane])[s->width * row + col] =
+        ((FLIF16ColorVal *) src->data[plane])[s->width * row + col];
+    }
+}
+
+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..5d137c12d4
--- /dev/null
+++ b/libavcodec/flif16_parser.c
@@ -0,0 +1,193 @@ 
+/*
+ * FLIF16 parser
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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_INIT_STATE = 0,
+    FLIF16_HEADER,
+    FLIF16_METADATA,
+    FLIF16_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_INIT_STATE:
+            if (!memcmp(flif16_header, buf + index, 4))
+                f->state = FLIF16_HEADER;
+            f->index++;
+            break;
+
+        case FLIF16_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_METADATA;
+                        f->varint = 0;
+                        f->index = 0;
+                        f->count = 0;
+                        continue;
+                    }
+                } else {
+                    f->count++;
+                }
+            }
+            f->index++;
+            break;
+
+        case FLIF16_METADATA:
+            if (f->index == 0) {
+                // Identifier for the bitstream chunk is a null byte.
+                if (buf[index] == 0) {
+                    f->state = FLIF16_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_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..02140bf637
--- /dev/null
+++ b/libavcodec/flif16_rangecoder.c
@@ -0,0 +1,798 @@ 
+/*
+ * Range coder for FLIF16
+ * Copyright (c) 2020, Anamitra Ghorui <aghorui@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"
+
+/*
+ * The coder requires a certain number of bytes for initiialization. buf
+ * provides it. gb is used by the coder functions for actual coding. This is
+ * done to be able to use leftover bytes from a previous packet. This prevents
+ * the creation of an additional buffer to append the new packet to the
+ * bytestream. This may seem cumbersome, but is done due to the fact that
+ * end of stream or end of frame is not easily determined.
+ */
+void ff_flif16_rac_init(FLIF16RangeCoder *rc, GetByteContext *gb, uint8_t *buf,
+                        uint8_t buf_size)
+{
+    // This function shouldn't be called if this condition is true.
+    if(buf_size < FLIF16_RAC_MAX_RANGE_BYTES)
+        return;
+
+    rc->range = FLIF16_RAC_MAX_RANGE;
+    rc->gb    = gb;
+
+    for (uint32_t r = FLIF16_RAC_MAX_RANGE; r > 1; r >>= 8) {
+        rc->low <<= 8;
+        rc->low |= *buf;
+        buf++;
+    }
+}
+
+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,
+                               int32_t min, int32_t len,
+                               int type,
+                               void *target)
+{
+    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->have    = 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;
+        if (m->forest[i]->data)
+            av_freep(&m->forest[i]->data);
+        if (m->forest[i]->leaves)
+            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. */
+    if(m->stack)
+        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..c308be5afe
--- /dev/null
+++ b/libavcodec/flif16_rangecoder.h
@@ -0,0 +1,393 @@ 
+/*
+ * Range coder for FLIF16
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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
+    GetByteContext *gb;
+    FLIF16MANIACChanceContext *curr_leaf;
+
+    uint_fast32_t range;
+    uint_fast32_t low;
+    uint16_t chance;
+    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;
+
+    // nz_int state management
+    int amin, amax, emax, e, have, left, minabs1, maxabs0, pos;
+    
+    // maniac_int state management
+    int oldmin, oldmax;
+} 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;
+
+void ff_flif16_rac_init(FLIF16RangeCoder *rc, GetByteContext *gb, uint8_t *buf,
+                        uint8_t buf_size);
+
+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, int32_t min, int32_t len,
+                               int type, void *target);
+
+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)
+{
+    uint32_t left;
+    while (rc->range <= FLIF16_RAC_MIN_RANGE) {
+        left = bytestream2_get_bytes_left(rc->gb);
+        if (!left) {
+            return 0;
+        }
+        rc->low <<= 8;
+        rc->range <<= 8;
+        rc->low |= bytestream2_get_byte(rc->gb);
+        if(!left) {
+            return 0;
+        } else {
+            left--;
+        }
+    }
+    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, type, target);
+            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
+        default:
+            break;
+        }
+    }
+    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; \
+    }
+
+#define MANIAC_GET(rc, m, prop, channel, min, max, target) \
+    if (!ff_flif16_maniac_read_int((rc), (m), (prop), (channel), (min), (max), (target))) {\
+        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..5f84876b74
--- /dev/null
+++ b/libavcodec/flif16_transform.c
@@ -0,0 +1,3009 @@ 
+/*
+ * Transforms for FLIF16.
+ * Copyright (c) 2020 Kartik K. Khullar <kartikkhullar840@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 and internal functions
+
+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
+    uint32_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;
+
+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 numPrevFrames;
+    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_assert0(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_assert0(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)
+{
+    const FLIF16Ranges *ranges = flif16_ranges[src_ctx->r_no];
+    *minv = ranges->min(src_ctx, p);
+    *maxv = 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);
+    if (*minv > *maxv)
+        *maxv = *minv;
+    *v = av_clip(*v, *minv, *maxv);
+}
+
+static void ff_static_close(FLIF16RangesContext *r_ctx)
+{
+    RangesPrivStatic *data = r_ctx->priv_data;
+    av_free(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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    switch (p) {
+    case FLIF16_PLANE_Y:
+        return 0;
+    case FLIF16_PLANE_CO:
+    case FLIF16_PLANE_CG:
+        return -4 * data->origmax4 + 1;
+    default:
+        return ranges->min(data->r_ctx, p);
+    }
+}
+
+static FLIF16ColorVal ff_ycocg_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivYCoCg *data = r_ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    switch (p) {
+    case FLIF16_PLANE_Y:
+    case FLIF16_PLANE_CO:
+    case FLIF16_PLANE_CG:
+        return 4 * data->origmax4 - 1;
+    default:
+        return 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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    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:
+        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_free(data->r_ctx->priv_data);
+    av_free(data->r_ctx);
+}
+
+/*
+ * PermutePlanesSubtract
+ */
+
+static FLIF16ColorVal ff_permuteplanessubtract_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    if (p == 0 || p > 2)
+        return ranges->min(data->r_ctx, data->permutation[p]);
+    return ranges->min(data->r_ctx, data->permutation[p]) -
+           ranges->max(data->r_ctx, data->permutation[0]);
+}
+
+static FLIF16ColorVal ff_permuteplanessubtract_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    if (p == 0 || p > 2)
+        return ranges->max(data->r_ctx, data->permutation[p]);
+    return ranges->max(data->r_ctx, data->permutation[p]) -
+           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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    if (p == 0 || p > 2) {
+        *minv = ranges->min(data->r_ctx, p);
+        *maxv = ranges->max(data->r_ctx, p);
+    } else {
+        *minv = ranges->min(data->r_ctx, data->permutation[p]) - prev_planes[0];
+        *maxv = 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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    return ranges->min(data->r_ctx, data->permutation[p]);
+}
+
+static FLIF16ColorVal ff_permuteplanes_max(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivPermuteplanes *data = r_ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    return 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_free(data->r_ctx->priv_data);
+    av_free(data->r_ctx);
+}
+
+/*
+ * Bounds
+ */
+
+static FLIF16ColorVal ff_bounds_min(FLIF16RangesContext *r_ctx, int p)
+{
+    RangesPrivBounds *data = r_ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    av_assert0(p < r_ctx->num_planes);
+    return FFMAX(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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    av_assert0(p < r_ctx->num_planes);
+    return FFMIN(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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    av_assert0(p < r_ctx->num_planes);
+    if (p == 0 || p == 3) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+        return;
+    }
+    ranges->minmax(data->r_ctx, p, prev_planes, minv, maxv);
+    if (*minv < data->bounds[p][0])
+        *minv = data->bounds[p][0];
+    if (*maxv > data->bounds[p][1])
+        *maxv = data->bounds[p][1];
+    if (*minv > *maxv) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+    }
+    av_assert0(*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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    if (p == 0 || p == 3) {
+        *minv = data->bounds[p][0];
+        *maxv = data->bounds[p][1];
+    } else {
+        ranges->snap(data->r_ctx, p, prev_planes, minv, maxv, v);
+        if (*minv < data->bounds[p][0])
+            *minv = data->bounds[p][0];
+        if (*maxv > data->bounds[p][1])
+            *maxv = data->bounds[p][1];
+        if (*minv > *maxv) {
+            *minv = data->bounds[p][0];
+            *maxv = data->bounds[p][1];
+        }
+    }
+    if (*v > *maxv)
+        *v = *maxv;
+    if (*v < *minv)
+        *v = *minv;
+}
+
+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_free(data->r_ctx->priv_data);
+    av_free(data->bounds);
+    av_free(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_free(data->r_ctx->priv_data);
+    av_free(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_assert0((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_free(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_free(cb->bucket1[i].snapvalues);
+        if (cb->bucket1[i].values)
+            ff_list_close(cb->bucket1[i].values);
+    }
+    av_free(cb->bucket1);
+
+    if (cb->bucket0.snapvalues)
+        av_free(cb->bucket0.snapvalues);
+    if (cb->bucket0.values)
+        ff_list_close(cb->bucket0.values);
+
+    if (cb->bucket3.snapvalues)
+        av_free(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_free(cb->bucket2[i][j].snapvalues);
+            if (cb->bucket2[i][j].values)
+                ff_list_close(cb->bucket2[i][j].values);
+        }
+        av_free(cb->bucket2[i]);
+    }
+    av_free(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_free(data->r_ctx->priv_data);
+    av_free(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->numPrevFrames;
+}
+
+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_free(data->ranges->priv_data);
+    }
+    av_free(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(unsigned int channels,
+                                                  unsigned int 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 = channels;
+    ctx->priv_data  = av_mallocz(r->priv_data_size);
+    if (!ctx->priv_data) {
+        av_free(ctx);
+        return NULL;
+    }
+    data = ctx->priv_data;
+    data->bounds = av_malloc_array(channels, sizeof(*data->bounds));
+    if (!data->bounds) {
+        av_free(ctx);
+        av_free(ctx->priv_data);
+        return NULL;
+    }
+    for (unsigned int i = 0; i < channels; ++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_free(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;
+    const FLIF16Ranges *src_ranges = flif16_ranges[r_ctx->r_no];
+
+    av_assert0(data);
+
+    if (r_ctx->num_planes < 3                                  ||
+        src_ranges->min(r_ctx, 0) == src_ranges->max(r_ctx, 0) ||
+        src_ranges->min(r_ctx, 1) == src_ranges->max(r_ctx, 1) ||
+        src_ranges->min(r_ctx, 2) == src_ranges->max(r_ctx, 2) ||
+        src_ranges->min(r_ctx, 0) < 0                          ||
+        src_ranges->min(r_ctx, 1) < 0                          ||
+        src_ranges->min(r_ctx, 2) < 0)
+        return 0;
+
+    data->origmax4 = FFMAX3(src_ranges->max(r_ctx, 0),
+                            src_ranges->max(r_ctx, 1),
+                            src_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 int transform_ycocg_forward(FLIF16Context *ctx,
+                                      FLIF16TransformContext *t_ctx,
+                                      FLIF16PixelData *pixel_data)
+{
+    int r, c;
+    FLIF16ColorVal RGB[3], YCOCG[3];
+
+    int height = ctx->height;
+    int width  = ctx->width;
+
+    for (r = 0; r<height; r++) {
+        for (c = 0; c<width; c++) {
+            ff_flif16_planes_get(ctx, pixel_data, RGB, r, c);
+
+            YCOCG[0] = (((RGB[0] + RGB[2])>>1) + RGB[1])>>1;
+            YCOCG[1] = RGB[0] - RGB[2];
+            YCOCG[2] = RGB[1] - ((RGB[0] + RGB[2])>>1);
+
+            ff_flif16_planes_set(ctx, pixel_data, YCOCG, r, c);
+        }
+    }
+    return 1;
+}
+
+static int 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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+
+    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, ranges->max(data->r_ctx, 0));
+            RGB[1] = av_clip(RGB[1], 0, ranges->max(data->r_ctx, 1));
+            RGB[2] = av_clip(RGB[2], 0, ranges->max(data->r_ctx, 2));
+
+            ff_flif16_planes_set(ctx, pixel_data, RGB, r, c);
+        }
+    }
+    return 1;
+}
+
+/*
+ * PermutePlanes
+ */
+
+static int transform_permuteplanes_init(FLIF16TransformContext *ctx,
+                                           FLIF16RangesContext *r_ctx)
+{
+    TransformPrivPermuteplanes *data = ctx->priv_data;
+    const FLIF16Ranges *ranges = flif16_ranges[r_ctx->r_no];
+    ff_flif16_chancecontext_init(&data->ctx_a);
+
+    if (r_ctx->num_planes     < 3 ||
+        ranges->min(r_ctx, 0) < 0 ||
+        ranges->min(r_ctx, 1) < 0 ||
+        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 int transform_permuteplanes_forward(FLIF16Context *ctx,
+                                              FLIF16TransformContext *t_ctx,
+                                              FLIF16PixelData *pixel_data)
+{
+    FLIF16ColorVal pixel[5];
+    int r, c, p;
+    int width  = ctx->width;
+    int height = ctx->height;
+    TransformPrivPermuteplanes *data = t_ctx->priv_data;
+    FLIF16ColorVal val;
+
+    for (r = 0; r < height; r++) {
+        for (c = 0; c < width; c++) {
+            for (p = 0; p < data->r_ctx->num_planes; p++)
+                pixel[p] = ff_flif16_pixel_get(ctx, pixel_data, 0, r, c);
+
+            val = pixel[data->permutation[0]];
+            ff_flif16_pixel_set(ctx, pixel_data, 0, r, c, val);
+            if (!data->subtract) {
+                for (p = 1; p<data->r_ctx->num_planes; p++) {
+                    val = pixel[data->permutation[p]];
+                    ff_flif16_pixel_set(ctx, pixel_data, p, r, c, val);
+                }
+            } else {
+                for (p = 1; p < 3 && p < data->r_ctx->num_planes; p++) {
+                    val = pixel[data->permutation[p]] - pixel[data->permutation[0]];
+                    ff_flif16_pixel_set(ctx, pixel_data, p, r, c, val);
+                }
+                for (p = 3; p < data->r_ctx->num_planes; p++) {
+                    val = pixel[data->permutation[p]];
+                    ff_flif16_pixel_set(ctx, pixel_data, p, r, c, val);
+                }
+            }
+        }
+    }
+    return 1;
+}
+
+static int 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;
+    const FLIF16Ranges *ranges = flif16_ranges[data->r_ctx->r_no];
+    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],
+                                  ranges->min(data->r_ctx, data->permutation[p]),
+                                  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]);
+            }
+        }
+    }
+    return 1;
+}
+
+/*
+ * 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_size[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;
+    const FLIF16Ranges *ranges = flif16_ranges[src_ctx->r_no];
+
+    for (; ctx->i < dec_ctx->num_planes; ctx->i++) {
+        switch (ctx->segment) {
+        case 0:
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, 0,
+                    ranges->max(src_ctx, ctx->i) - ranges->min(src_ctx, ctx->i),
+                    &nb, FLIF16_RAC_NZ_INT);
+            nb += 1;
+            data->min = 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,
+                        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_free(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_free(src_ctx->priv_data);
+    av_free(src_ctx);
+
+    return r_ctx;
+}
+
+static int 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_assert0(P < (int) palette_size);
+                ff_flif16_pixel_set(ctx, frame, p, r, c, palette[P]);
+            }
+        }
+    }
+    return 1;
+}
+
+static void transform_channelcompact_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivChannelcompact *data = ctx->priv_data;
+    for (unsigned int i = 0; i < 4; i++) {
+        av_free(data->cpalette[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;
+    const FLIF16Ranges *ranges = flif16_ranges[src_ctx->r_no];
+    int max;
+
+    for (; ctx->i < dec_ctx->num_planes; ctx->i++) {
+        switch (ctx->segment) {
+        case 0:
+            ranges->min(src_ctx, ctx->i);
+            ranges->max(src_ctx, ctx->i);
+            RAC_GET(&dec_ctx->rc, &data->ctx_a, ranges->min(src_ctx, ctx->i),
+                    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,
+                    ranges->max(src_ctx, ctx->i), &max, FLIF16_RAC_GNZ_INT);
+            if (data->min > max)
+                return 0;
+            if (data->min < ranges->min(src_ctx, ctx->i))
+                return 0;
+            if (max > 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_free(r_ctx);
+            return NULL;
+        }
+        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_free(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_free(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 int 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_assert0(P < data->size);
+            av_assert0(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]);
+        }
+    }
+    return 1;
+}
+
+static void transform_palette_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivPalette *data = ctx->priv_data;
+    av_free(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_free(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 int 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_assert0(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]);
+        }
+    }
+    return 1;
+}
+
+static void transform_palettealpha_close(FLIF16TransformContext *ctx)
+{
+    TransformPrivPalettealpha *data = ctx->priv_data;
+    av_free(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_free(temp);
+                } else if (temp == cb->values_last) {
+                    cb->values_last = prev;
+                    cb->values_last->next = NULL;
+                    av_free(temp);
+                } else if (!prev) {
+                    cb->values = temp->next;
+                    av_free(temp);
+                }
+                cb->values_size--;
+                break;
+            }
+            prev = temp;
+        }
+        if (cb->values_size == 0) {
+            cb->min = 10000;
+            cb->max = -10000;
+            return 1;
+        }
+        av_assert0(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_free(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_free(cb);
+        av_free(cb->bucket1);
+        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_free(cb);
+            av_free(cb->bucket1);
+            av_free(cb->bucket2);
+            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_free(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);
+            if (rmin < *smin)
+                *smin = rmin;
+            if (rmax > *smax)
+                *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);
+                if (rmin < *smin)
+                    *smin = rmin;
+                if (rmax > *smax)
+                    *smax = rmax;
+            }
+        }
+    }
+    else if (p == FLIF16_PLANE_ALPHA) {
+        ff_flif16_ranges_minmax(src_ctx, p, pixel, smin, smax);
+    }
+}
+
+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) {
+    case 2:
+        for (; data->j < cb->bucket2_size; data->j++) {
+            data->pixelL[1] = cb->min1;
+            data->pixelU[1] = cb->min1 + 4 - 1;
+            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_free(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;
+        goto end;
+    }
+
+    end:
+    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_free(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_free(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_free(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_free(data->b);
+    av_free(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_free(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->numPrevFrames = 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 int 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;
+
+    return 1;
+}
+
+const FLIF16Transform flif16_transform_channelcompact = {
+    .priv_data_size = sizeof(TransformPrivChannelcompact),
+    .init           = &transform_channelcompact_init,
+    .read           = &transform_channelcompact_read,
+    .meta           = &transform_channelcompact_meta,
+    .forward        = NULL,
+    .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,
+    .forward        = &transform_ycocg_forward,
+    .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,
+    .forward        = &transform_permuteplanes_forward,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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,
+    .forward        = NULL,
+    .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_free(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);
+}
+
+int 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)
+        return trans->reverse(ctx, t_ctx, frame, stride_row, stride_col);
+    else
+        return 1;
+}
+
+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_free(ctx->priv_data);
+    av_freep(&ctx);
+}
diff --git a/libavcodec/flif16_transform.h b/libavcodec/flif16_transform.h
new file mode 100644
index 0000000000..0f1011a5ff
--- /dev/null
+++ b/libavcodec/flif16_transform.h
@@ -0,0 +1,121 @@ 
+/*
+ * Transforms for FLIF16.
+ * Copyright (c) 2020 Kartik K. Khullar <kartikkhullar840@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[14];
+extern const FLIF16Transform *flif16_transforms[13];
+
+FLIF16RangesContext *ff_flif16_ranges_static_init(unsigned int channels,
+                                                  unsigned int 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, FLIF16RangesContext *);
+
+void ff_flif16_transform_configure(FLIF16TransformContext *, const int);
+
+int ff_flif16_transform_read(FLIF16Context *, FLIF16TransformContext *,
+                             FLIF16RangesContext *);
+
+FLIF16RangesContext* ff_flif16_transform_meta(FLIF16Context *,
+                                              FLIF16PixelData *,
+                                              uint32_t,
+                                              FLIF16TransformContext *,
+                                              FLIF16RangesContext *);
+
+int ff_flif16_transform_reverse(FLIF16Context *, FLIF16TransformContext*,
+                                FLIF16PixelData*, uint8_t, uint8_t);
+
+void ff_flif16_transforms_close(FLIF16TransformContext *);
+
+#endif /* FLIF16_TRANSFORM_H */
diff --git a/libavcodec/flif16dec.c b/libavcodec/flif16dec.c
new file mode 100644
index 0000000000..5bdc14e20b
--- /dev/null
+++ b/libavcodec/flif16dec.c
@@ -0,0 +1,1787 @@ 
+/*
+ * FLIF16 Decoder
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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/crc.h"
+#include "libavutil/imgutils.h"
+
+typedef enum FLIF16States {
+    FLIF16_HEADER = 0,
+    FLIF16_SECONDHEADER,
+    FLIF16_TRANSFORM,
+    FLIF16_ROUGH_PIXELDATA,
+    FLIF16_MANIAC,
+    FLIF16_PIXELDATA,
+    FLIF16_OUTPUT,
+    FLIF16_EOS
+} FLIF16States;
+
+/*
+ * 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;
+    GetByteContext gb;
+
+    // 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
+    uint8_t  plane_mode[MAX_PLANES];
+
+    // Transform flags
+    uint8_t framedup;
+    uint8_t frameshape;
+    uint8_t framelookback;
+
+    /* End Inheritance from FLIF16Context */
+
+    AVFrame *out_frame;
+    FLIF16PixelData  *frames;
+    int64_t pts;
+    uint32_t out_frames_count;
+
+    FLIF16States 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;
+    uint8_t buf[FLIF16_RAC_MAX_RANGE_BYTES]; ///< Storage for initial RAC buffer
+    uint8_t buf_count;                       ///< Count for initial RAC buffer
+
+    // 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
+    FLIF16RangesContext *prev_range;
+
+    // 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(CTX_CAST(s));
+
+    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)
+{
+    uint32_t temp;
+    FLIF16DecoderContext *s = avctx->priv_data;
+
+    switch (s->segment) {
+    case 0:
+        s->buf_count += bytestream2_get_buffer(&s->gb, s->buf + s->buf_count,
+                                               FFMIN(bytestream2_get_bytes_left(&s->gb),
+                                               (FLIF16_RAC_MAX_RANGE_BYTES - s->buf_count)));
+        if (s->buf_count < FLIF16_RAC_MAX_RANGE_BYTES)
+            return AVERROR(EAGAIN);
+
+        ff_flif16_rac_init(&s->rc, &s->gb, s->buf, s->buf_count);
+        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;
+    uint8_t 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, 13, &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;
+
+    if (ret = ff_flif16_planes_init(CTX_CAST(s), s->frames, s->plane_mode,
+                              const_plane_value, s->framelookback) < 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 width = s->width;
+    int which = 0;
+    int index = 0;
+
+    FLIF16ColorVal *properties = s->properties;
+    FLIF16RangesContext *ranges_ctx = s->range;
+    FLIF16ColorVal *min = &s->min;
+    FLIF16ColorVal *max = &s->max;
+
+    uint32_t c = s->c;
+
+    if (p < 3) {
+        for (int pp = 0; pp < p; pp++) {
+            properties[index++] = PIXEL_GET(s, fr, pp, r, s->c);
+        }
+        if (ranges_ctx->num_planes > 3) {
+            properties[index++] = PIXEL_GET(s, fr, 3, r, s->c);
+        }
+    }
+
+    left = (nobordercases || 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 && 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(ranges_ctx, p, properties, min, max, &guess);
+
+    if (guess == gradientTL)
+        which = 0;
+    else if (guess == left)
+        which = 1;
+    else if (guess == top)
+        which = 2;
+
+    properties[index++] = guess;
+    properties[index++] = which;
+
+    if (nobordercases || (s->c > 0 && r > 0)) {
+        properties[index++] = left - topleft;
+        properties[index++] = topleft - top;
+    } else {
+        properties[index++] = 0;
+        properties[index++] = 0;
+    }
+
+    if (nobordercases || (c + 1 < width && r > 0)) {
+        properties[index++] = top - PIXEL_GET(s, fr, p, r - 1, c + 1);
+    } else {
+        properties[index++] = 0;
+    }
+
+    if (nobordercases || r > 1) {
+        properties[index++] = PIXEL_GET(s, fr, p, r - 2, s->c) - top;
+    } else {
+        properties[index++] = 0;
+    }
+
+    if (nobordercases || c > 1) {
+        properties[index++] = PIXEL_GET(s, fr, p, r, s->c - 2) - left;
+    } else {
+        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 (ff_flif16_ranges_min(s->range, s->curr_plane) >=
+                ff_flif16_ranges_max(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
+
+    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);
+        }
+    }
+    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;
+
+
+    // 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; //break;
+            }
+        }
+
+        // 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 && !(s->ia % 2)) {
+                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;
+    if (s->framedelay)
+        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,
+    .close          = flif16_decode_end,
+    .priv_data_size = sizeof(FLIF16DecoderContext),
+    .decode         = flif16_decode_frame,
+    .capabilities   = AV_CODEC_CAP_DELAY,
+    //.caps_internal  = 0,
+    .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 a3f9f828ee..5bdfdce363 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 100
+#define LIBAVCODEC_VERSION_MINOR 101
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 610d43ca99..e5f653e06e 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -192,6 +192,7 @@  OBJS-$(CONFIG_FLAC_DEMUXER)              += flacdec.o rawdec.o \
 OBJS-$(CONFIG_FLAC_MUXER)                += flacenc.o flacenc_header.o \
                                             vorbiscomment.o
 OBJS-$(CONFIG_FLIC_DEMUXER)              += flic.o
+OBJS-$(CONFIG_FLIF_DEMUXER)            += flifdec.o
 OBJS-$(CONFIG_FLV_DEMUXER)               += flvdec.o
 OBJS-$(CONFIG_LIVE_FLV_DEMUXER)          += flvdec.o
 OBJS-$(CONFIG_FLV_MUXER)                 += flvenc.o avc.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index b7e59ae170..8759431e08 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -150,6 +150,7 @@  extern AVOutputFormat ff_fits_muxer;
 extern AVInputFormat  ff_flac_demuxer;
 extern AVOutputFormat ff_flac_muxer;
 extern AVInputFormat  ff_flic_demuxer;
+extern AVInputFormat  ff_flif_demuxer;
 extern AVInputFormat  ff_flv_demuxer;
 extern AVOutputFormat ff_flv_muxer;
 extern AVInputFormat  ff_live_flv_demuxer;
diff --git a/libavformat/flifdec.c b/libavformat/flifdec.c
new file mode 100644
index 0000000000..8fbedd6b58
--- /dev/null
+++ b/libavformat/flifdec.c
@@ -0,0 +1,435 @@ 
+/*
+ * FLIF16 demuxer
+ * Copyright (c) 2020 Anamitra Ghorui <aghorui@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
+ * FLIF demuxer.
+ */
+
+#include "avformat.h"
+#include "libavutil/common.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavcodec/exif.h"
+
+#include "libavcodec/flif16.h"
+#include "libavcodec/flif16_rangecoder.h"
+
+#include "config.h"
+
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
+/*
+ * FLIF's reference encoder currently encodes metadata as a raw DEFLATE stream
+ * (RFC 1951). In order to decode a raw deflate stream using Zlib, inflateInit2
+ * must be used with windowBits being between -8 .. -15.
+ */
+#define ZLIB_WINDOW_BITS -15
+#define BUF_SIZE 4096
+
+typedef struct FLIFDemuxContext {
+    const AVClass *class;
+#if CONFIG_ZLIB
+    z_stream stream;
+    uint8_t active;
+#endif
+} FLIFDemuxContext;
+
+
+#if CONFIG_ZLIB
+static int flif_inflate(FLIFDemuxContext *s, uint8_t *buf, int buf_size,
+                        uint8_t **out_buf, int *out_buf_size)
+{
+    int ret;
+    z_stream *stream = &s->stream;
+
+    if (!s->active) {
+        s->active = 1;
+        stream->zalloc   = Z_NULL;
+        stream->zfree    = Z_NULL;
+        stream->opaque   = Z_NULL;
+        stream->avail_in = 0;
+        stream->next_in  = Z_NULL;
+        ret = inflateInit2(stream, ZLIB_WINDOW_BITS);
+
+        if (ret != Z_OK)
+            return ret;
+
+        *out_buf_size = buf_size;
+        *out_buf = av_realloc_f(*out_buf, *out_buf_size, 1);
+        if (!*out_buf)
+            return AVERROR(ENOMEM);
+    }
+
+    stream->next_in  = buf;
+    stream->avail_in = buf_size;
+
+    do {
+        while (stream->total_out >= (*out_buf_size - 1)) {
+            *out_buf = av_realloc_f(*out_buf, (*out_buf_size) * 2, 1);
+            if (!out_buf)
+                return AVERROR(ENOMEM);
+            *out_buf_size *= 2;
+        }
+
+        stream->next_out  = *out_buf + stream->total_out;
+        stream->avail_out = *out_buf_size - stream->total_out - 1;
+     
+        ret = inflate(stream, Z_PARTIAL_FLUSH);
+
+        switch (ret) {
+        case Z_NEED_DICT:
+        case Z_DATA_ERROR:
+            (void)inflateEnd(stream);
+            return AVERROR_INVALIDDATA;
+        case Z_MEM_ERROR:
+            (void)inflateEnd(stream);
+            return AVERROR(ENOMEM);
+        }
+    } while (stream->avail_in > 0);
+
+    if (ret == Z_STREAM_END) {
+        s->active = 0;
+        (*out_buf)[stream->total_out] = '\0';
+        (void) inflateEnd(stream);
+    } else
+        ret = AVERROR(EAGAIN);
+
+    return ret; // Return Z_BUF_ERROR/EAGAIN as long as input is incomplete.
+}
+#endif
+
+
+static int flif_read_exif(void *logctx, uint8_t *buf, int buf_size, AVDictionary **d)
+{
+    uint8_t le;
+    uint32_t temp;
+    int ret;
+    GetByteContext gb;
+    // Read exif header
+    if (memcmp("Exif", buf, 4))
+        return AVERROR_INVALIDDATA;
+
+    buf += 6;
+
+    // Figure out endianness
+    if (buf[0] == 'M' && buf[1] == 'M')
+        le = 0;
+    else if (buf[0] == 'I' && buf[1] == 'I')
+        le = 1;
+    else
+        return AVERROR_INVALIDDATA;
+
+    buf += 2;
+
+    bytestream2_init(&gb, buf, buf_size - 8);
+    temp = ff_tget_short(&gb, le);
+
+    // Check TIFF marker
+    if (temp != 0x002A)
+        return AVERROR_INVALIDDATA;
+
+    buf += 2;
+
+    if (le)
+        temp = bytestream2_get_le32(&gb);
+    else
+        temp = bytestream2_get_be32(&gb);
+
+    // Subtract read bytes, then skip
+    bytestream2_skip(&gb, temp - 8);
+
+    ret = ff_exif_decode_ifd(logctx, &gb, le, 0, d);
+    
+    return ret;
+}
+
+static int flif16_probe(const AVProbeData *p)
+{
+    uint32_t vlist[3] = {0};
+    unsigned int count = 0, pos = 0;
+
+    // Magic Number
+    if (memcmp(p->buf, flif16_header, 4)) {
+        return 0;
+    }
+
+    for(int i = 0; i < 2 + (((p->buf[4] >> 4) > 4) ? 1 : 0); i++) {
+        while (p->buf[5 + pos] > 127) {
+            if (!(count--)) {
+                return 0;
+            }
+            VARINT_APPEND(vlist[i], p->buf[5 + pos]);
+            ++pos;
+        }
+        VARINT_APPEND(vlist[i], p->buf[5 + pos]);
+        count = 0;
+    }
+
+    if (!((vlist[0] + 1) && (vlist[1] + 1)))
+        return 0;
+
+    if (((p->buf[4] >> 4) > 4) && !(vlist[2] + 2))
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+static int flif16_read_header(AVFormatContext *s)
+{
+    FLIFDemuxContext *dc = s->priv_data;
+    GetByteContext gb;
+    FLIF16RangeCoder rc = (FLIF16RangeCoder) {0};
+
+    AVIOContext     *pb  = s->pb;
+    AVStream        *st;
+
+    int64_t duration = 0;
+    uint32_t vlist[3] = {0};
+    uint32_t flag, animated, temp;
+    uint32_t bpc = 0;
+    uint32_t metadata_size = 0;
+    int out_buf_size = 0;
+    int buf_size = 0;
+    unsigned int count = 4;
+    int ret;
+    int format;
+    int segment = 0, i = 0;
+    uint8_t tag[5] = {0};
+    uint8_t buf[BUF_SIZE];
+    uint8_t *out_buf = NULL;
+    uint8_t loops = 0;
+    uint8_t num_planes;
+    uint8_t num_frames;
+
+#if !CONFIG_ZLIB
+    av_log(s, AV_LOG_WARNING, "ffmpeg has not been compiled with Zlib. Metadata may not be decoded.\n");
+#endif
+
+    // Magic Number
+    if (avio_rl32(pb) != (*((uint32_t *) flif16_header))) {
+        av_log(s, AV_LOG_ERROR, "bad magic number\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    st = avformat_new_stream(s, NULL);
+    if (!st)
+        return AVERROR(ENOMEM);
+    flag = avio_r8(pb);
+    animated = (flag >> 4) > 4;
+    duration = !animated;
+    bpc = avio_r8(pb); // Bytes per channel
+
+    num_planes = flag & 0x0F;
+
+    for (int i = 0; i < (2 + animated); i++) {
+        while ((temp = avio_r8(pb)) > 127) {
+            if (!(count--))
+                return AVERROR_INVALIDDATA;
+            VARINT_APPEND(vlist[i], temp);
+        }
+        VARINT_APPEND(vlist[i], temp);
+        count = 4;
+    }
+
+    vlist[0]++;
+    vlist[1]++;
+    if (animated)
+        vlist[2] += 2;
+    else
+        vlist[2] = 1;
+
+    num_frames = vlist[2];
+
+    while ((temp = avio_r8(pb))) {
+        // Get metadata identifier
+        tag[0] = temp;
+        for(int i = 1; i <= 3; i++)
+            tag[i] = avio_r8(pb);
+
+        // Read varint
+        while ((temp = avio_r8(pb)) > 127) {
+            if (!(count--))
+                return AVERROR_INVALIDDATA;
+            VARINT_APPEND(metadata_size, temp);
+        }
+        VARINT_APPEND(metadata_size, temp);
+        count = 4;
+
+#if CONFIG_ZLIB
+        /*
+         * Decompression Routines
+         * There are 3 supported metadata chunks currently in FLIF: eXmp, eXif,
+         * and iCCp.
+         */
+        while (metadata_size > 0) {
+            if ((buf_size = avio_read_partial(pb, buf, FFMIN(BUF_SIZE, metadata_size))) < 0)
+                return buf_size;
+            metadata_size -= buf_size;
+            if((ret = flif_inflate(dc, buf, buf_size, &out_buf, &out_buf_size)) < 0 &&
+                ret != AVERROR(EAGAIN)) {
+                if (ret == AVERROR(ENOMEM) || ret == AVERROR_INVALIDDATA)
+                    return ret;
+                av_log(s, AV_LOG_ERROR, "could not decode metadata segment: %s\n", tag);
+                avio_skip(pb, metadata_size);
+                goto metadata_fail;
+            }
+        }
+
+        switch (*((uint32_t *) tag)) {
+        case MKTAG('e', 'X', 'i','f'):
+            ret = flif_read_exif(s, out_buf, out_buf_size, &s->metadata);
+            if (ret < 0)
+                av_log(s, AV_LOG_WARNING, "metadata may be corrupted\n");
+            break;
+
+        case MKTAG('i','C','C','P'):
+            break;
+
+        default:
+            av_dict_set(&s->metadata, tag, out_buf, 0);
+            break;
+        }
+#else
+        avio_skip(pb, metadata_size);
+#endif
+        metadata_fail:
+            continue;
+    }
+
+    av_freep(&out_buf);
+
+    avio_read(pb, buf, FLIF16_RAC_MAX_RANGE_BYTES);
+    ff_flif16_rac_init(&rc, NULL, buf, FLIF16_RAC_MAX_RANGE_BYTES);
+    ret = avio_read_partial(pb, buf, BUF_SIZE);
+    bytestream2_init(&gb, buf, ret);
+    rc.gb = &gb;
+
+    while (1) {
+        switch (segment) {
+        case 0:
+            if (bpc == '0') {
+                bpc = 0;
+                for (; i < num_planes; i++) {
+                    RAC_GET(&rc, NULL, 1, 15, &temp, FLIF16_RAC_UNI_INT8);
+                    bpc = FFMAX(bpc, (1 << temp) - 1);
+                }
+                i = 0;
+            } else
+                bpc = (bpc == '1') ? 255 : 65535;
+            if (num_frames < 2)
+                goto end;
+            segment++;
+
+        case 1:
+            if (num_planes > 3) {
+                RAC_GET(&rc, NULL, 0, 1, &temp, FLIF16_RAC_UNI_INT8);
+            }
+            segment++;
+
+        case 2:
+            if (num_frames > 1) {
+                RAC_GET(&rc, NULL, 0, 100, &loops, FLIF16_RAC_UNI_INT8);
+            } else
+                loops = 1;
+            segment++;
+
+        case 3:
+            if (num_frames > 1) {
+                for (; i < num_frames; ++i) {
+                    temp = 0;
+                    RAC_GET(&rc, NULL, 0, 60000, &(temp), FLIF16_RAC_UNI_INT16);
+                    duration += temp;
+                }
+                i = 0;
+            } else
+                duration = 1;
+            goto end;
+        }
+
+        need_more_data:
+        if ((ret = avio_read_partial(pb, buf, BUF_SIZE)) < 0)
+            return ret;
+        bytestream2_init(&gb, buf, ret);
+    }
+
+    end:
+    if (bpc > 65535) {
+        av_log(s, AV_LOG_ERROR, "depth per channel greater than 16 bits not supported\n");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    // The minimum possible delay in a FLIF16 image is 1 millisecond.
+    // Therefore time base is 10^-3, i.e. 1/1000
+    format = flif16_out_frame_type[FFMIN(num_planes, 4)][bpc > 255];
+    avpriv_set_pts_info(st, 64, 1, 1000);
+    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codecpar->codec_id   = AV_CODEC_ID_FLIF16;
+    st->codecpar->width      = vlist[0];
+    st->codecpar->height     = vlist[1];
+    st->codecpar->format     = format;
+    st->duration             = duration * loops;
+    st->start_time           = 0;
+    st->nb_frames            = vlist[2];
+    st->need_parsing         = 1;
+
+    // Jump to start because flif16 decoder needs header data too
+    if (avio_seek(pb, 0, SEEK_SET) != 0)
+        return AVERROR(EIO);
+    return 0;
+}
+
+
+static int flif16_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    AVIOContext *pb  = s->pb;
+    int ret;
+    ret = av_get_packet(pb, pkt, avio_size(pb));
+    return ret;
+}
+
+
+static const AVOption options[] = {
+    { NULL }
+};
+
+static const AVClass demuxer_class = {
+    .class_name = "FLIF demuxer",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+    .category   = AV_CLASS_CATEGORY_DEMUXER,
+};
+
+AVInputFormat ff_flif_demuxer = {
+    .name           = "flif",
+    .long_name      = NULL_IF_CONFIG_SMALL("Free Lossless Image Format (FLIF)"),
+    .priv_data_size = sizeof(FLIFDemuxContext),
+    .extensions     = "flif",
+    .read_probe     = flif16_probe,
+    .read_header    = flif16_read_header,
+    .read_packet    = flif16_read_packet,
+    .priv_class     = &demuxer_class,
+};
diff --git a/libavformat/version.h b/libavformat/version.h
index 4d31e1ec3e..aa309ecc77 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@ 
 // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  58
-#define LIBAVFORMAT_VERSION_MINOR  50
+#define LIBAVFORMAT_VERSION_MINOR  51
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \