diff mbox series

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

Message ID 20200822183935.1991-1-aghorui@teknik.io
State New
Headers show
Series [FFmpeg-devel,v6,1/4] lavc: 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. 22, 2020, 6:39 p.m. UTC
v2: Fix faulty patch
v3: Fix addressed errors, Add interlaced decoding support
v4: Fix Further cosmetics, C.Bucket Transform reading errors, Atomise patch
v5: Fix faulty patch
v6: Address pointed out errors, use av_freep everywhere, further cosmetics,
    redundancies.

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

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

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

Comments

Moritz Barsnick Aug. 26, 2020, 1:14 p.m. UTC | #1
On Sun, Aug 23, 2020 at 00:09:32 +0530, Anamitra Ghorui wrote:
> v2: Fix faulty patch
> v3: Fix addressed errors, Add interlaced decoding support
> v4: Fix Further cosmetics, C.Bucket Transform reading errors, Atomise patch
> v5: Fix faulty patch
> v6: Address pointed out errors, use av_freep everywhere, further cosmetics,
>     redundancies.

These comments don't belong in the commit or the e-mail corresponding
to the commit. So either in the "0/4" e-mail, or below the "---" below.

> Test files are available here: https://0x0.st/iYs_.zip
>
> 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>
> ---

Place comments here.

> +    if (plane == 1 || plane == 2){

Please fix the bracket style -> ") {"

> +    if (plane != 2) {
> +      prop_ranges[top].min = mind;
> +      prop_ranges[top++].max = maxd;
> +      prop_ranges[top].min = mind;
> +      prop_ranges[top++].max = maxd;
> +    }

Incorrect indentation.

> +    for(uint8_t i = 0; i < (lookback ? MAX_PLANES : num_planes); i++) {

Bracket style -> "for ("

> +/*
> + * All constant plane pixel setting should be illegal in theory.

settings

> +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;

Isn't this "return 0" dead code?

> +    if(bytestream2_get_bytes_left(gb) < FLIF16_RAC_MAX_RANGE_BYTES)

Bracket style -> "if ("

> +uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
> +                               uint8_t *target)
> +{
> +    return ff_flif16_rac_get(rc, rc->range >> 1, target);
> +}

If this is called often, you may want to mark it inline.

> +uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc,
> +                                   uint64_t b12, uint8_t *target)
> +{
> +    uint32_t ret = ((rc->range) * b12 + 0x800) >> 12;

I don't think rc->range needs to be bracketed.

> +    if(!ff_flif16_rac_renorm(rc))

Bracket style.

> +#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;                                                   \
> +    }

Functions are usually defined with a do{} while(0) wrapper. See the
style in libavutil/intreadwrite.h.

> +    return ret;
> +
> +}

Drop the empty line. ;-)

> +    if(!ff_flif16_rac_renorm(rc))

Bracket style.

> +        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;
> +            }

A case label in the middle of an if() (in a while() loop) is not
readable to me. ;-)

> +    m->stack_top = 0;
> +    rc->segment2 = 0;
> +    return 0;
> +
> +    need_more_data:
> +    return AVERROR(EAGAIN);

I believe the label needs to be left-aligned.

> +        if(!m->forest[channel]->leaves)

Bracket style.

> +        if(!rc->curr_leaf) {

Ditto.

> +
> +    end:
> +    rc->curr_leaf = NULL;

"goto" label left alignment

> + * probability model. The other (simpler) model and this model ane non

*are

> +#define RAC_GET(rc, ctx, val1, val2, target, type) \
> +    if (!ff_flif16_rac_process((rc), (ctx), (val1), (val2), (target), (type))) { \
> +        goto need_more_data; \
> +    }

do{} while(0)

> +#define MANIAC_GET(rc, m, prop, channel, min, max, target) \
> +    { \
> +        int ret = ff_flif16_maniac_read_int((rc), (m), (prop), (channel), (min), (max), (target)); \
> +        if (ret < 0) { \
> +            return ret; \
> +        } else if (!ret) { \
> +            goto need_more_data; \
> +        } \
> +    }

Ditto.



I give up here. There are further 4000+ lines to review. What an
impressive patch.

Just this:

> +static void transform_palette_reverse(FLIF16Context *ctx,
> +                                     FLIF16TransformContext *t_ctx,
> +                                     FLIF16PixelData *frame,
> +                                     uint32_t stride_row,
> +                                     uint32_t stride_col)
> +{
> +    int r, c;
> +    int P;

ffmpeg doesn't use capitals in variable names.

Cheers,
Moritz
Anamitra Ghorui Aug. 26, 2020, 4:31 p.m. UTC | #2
Hello,
Thanks for the review. Please see replies below.

On 26/08/2020 18:44, Moritz Barsnick wrote:
> On Sun, Aug 23, 2020 at 00:09:32 +0530, Anamitra Ghorui wrote:
>> v2: Fix faulty patch
>> v3: Fix addressed errors, Add interlaced decoding support
>> v4: Fix Further cosmetics, C.Bucket Transform reading errors, Atomise patch
>> v5: Fix faulty patch
>> v6: Address pointed out errors, use av_freep everywhere, further cosmetics,
>>      redundancies.
> 
> These comments don't belong in the commit or the e-mail corresponding
> to the commit. So either in the "0/4" e-mail, or below the "---" below.
> 
>> Test files are available here: https://0x0.st/iYs_.zip
>>
>> 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>
>> ---
> 
> Place comments here.
>
Will do as a separate E-Mail

>> +    if (plane == 1 || plane == 2){
> 
> Please fix the bracket style -> ") {"
> 
>> +    if (plane != 2) {
>> +      prop_ranges[top].min = mind;
>> +      prop_ranges[top++].max = maxd;
>> +      prop_ranges[top].min = mind;
>> +      prop_ranges[top++].max = maxd;
>> +    }
> 
> Incorrect indentation.
> 
>> +    for(uint8_t i = 0; i < (lookback ? MAX_PLANES : num_planes); i++) {
> 
> Bracket style -> "for ("
> 
>> +/*
>> + * All constant plane pixel setting should be illegal in theory.
> 
> settings
> 
>> +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;
> 
> Isn't this "return 0" dead code?
> 
>> +    if(bytestream2_get_bytes_left(gb) < FLIF16_RAC_MAX_RANGE_BYTES)
> 
> Bracket style -> "if ("
> 

Will fix all above

>> +uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
>> +                               uint8_t *target)
>> +{
>> +    return ff_flif16_rac_get(rc, rc->range >> 1, target);
>> +}
> 
> If this is called often, you may want to mark it inline.
> 

On Nicolas George's suggestion, I had dropped the inline keyword because
the number of inline functions used may put stress on the memory cache.
I think I should only inine the "higher" functions and not the "lower"
functions like ff_flif16_rac_get in this case. Should I?

>> +uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc,
>> +                                   uint64_t b12, uint8_t *target)
>> +{
>> +    uint32_t ret = ((rc->range) * b12 + 0x800) >> 12;
> 
> I don't think rc->range needs to be bracketed.
> 
>> +    if(!ff_flif16_rac_renorm(rc))
> 
> Bracket style.

Will fix

>> +#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;                                                   \
>> +    }
> 
> Functions are usually defined with a do{} while(0) wrapper. See the
> style in libavutil/intreadwrite.h.
Please see the section below regarding intermittent packet resuming.

>> +    return ret;
>> +
>> +}
> 
> Drop the empty line. ;-)
> 
>> +    if(!ff_flif16_rac_renorm(rc))
> 
> Bracket style.
> 

Will fix

>> +        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;
>> +            }
> 
> A case label in the middle of an if() (in a while() loop) is not
> readable to me. ;-)

Please see the section below regarding intermittent packet resuming.

>> +    m->stack_top = 0;
>> +    rc->segment2 = 0;
>> +    return 0;
>> +
>> +    need_more_data:
>> +    return AVERROR(EAGAIN);
> 
> I believe the label needs to be left-aligned.
> 
>> +        if(!m->forest[channel]->leaves)
> 
> Bracket style.
> 
>> +        if(!rc->curr_leaf) {
> 
> Ditto.
> 
>> +
>> +    end:
>> +    rc->curr_leaf = NULL;
> 
> "goto" label left alignment
> 
>> + * probability model. The other (simpler) model and this model ane non
> 
> *are
>

Will fix

>> +#define RAC_GET(rc, ctx, val1, val2, target, type) \
>> +    if (!ff_flif16_rac_process((rc), (ctx), (val1), (val2), (target), (type))) { \
>> +        goto need_more_data; \
>> +    }
> 
> do{} while(0)
> 
>> +#define MANIAC_GET(rc, m, prop, channel, min, max, target) \
>> +    { \
>> +        int ret = ff_flif16_maniac_read_int((rc), (m), (prop), (channel), (min), (max), (target)); \
>> +        if (ret < 0) { \
>> +            return ret; \
>> +        } else if (!ret) { \
>> +            goto need_more_data; \
>> +        } \
>> +    }
> 
> Ditto.
>

The FLIF format has no way of predetermining the end of the bitstream
without actually decompressing it first and reading all frame data. In
addition to that, frame data has been organised in a non-contiguous
manner (See also: [1]). In short, a pixel is encoded in this manner
for non interlaced images:

    for i in channels
        for j in rows
            for k in frames
                for l in columns
                    encode(pixel, i, j, k ,l)

Similarly, for non interlaced images, the successive "quality" blocks or
zoomlevels are coded in order.
Please see [2] for a more visual representation.

Therefore there is no way to split the image data into frames without
reading in the whole of the image data first. 
Hence what we had decided to do is write the decoder in a manner that it
will be able to take in any arbitrary packet size, and be able to resume
at any point the packet ends i.e. the range decoder says that it needs
more data.

To do that, decoding related functions which call the RAC have
FLIF16DecoderContext passed to them. The struct contains the member
segment. The function then contains a switch case which uses s->segment
to restore the function control to where it left off. All stack
allocated variables that may be otherwise lost are put in the context
instead. Similarly for other subsystem contexts.

I agree that the case statements between if statements and while
statements are not very good looking, but this allows us to extract the
function logic without having to set the, or look at the state
management variables and statements. Trying to make do without the
interleaving statements would cause us to set s->segment many times,
set multiple switch statements etc.

    func(FLIF16XYZContext *s, ...)
    {
        // Init stuff
        ...
        
        switch (s->segment) {
        /* Note how cases are right before RAC calls */
        case 1:
            RAC_GET(...);
            ...
        case 2:
            RAC_GET(...);
            ...
        ...
        }

        // End stuff
        ...
        return 0;

    need more data:
        ...
        return AVERROR(EAGAIN);
    }

I get your point about using do {} while (0) to break the sequence, it
does make sense now to ditch the need_more_data entirely and hence free 
it from having to using a specific goto label, however it introduces an 
additional level of indentation. This is how majority of the decoder 
logic and transform logic has been written, so it may make the code 
look a bit bulkier. Should I do it anyway?

    func(FLIF16XYZContext *s, ...)
    {
        // Init stuff
        ...

        do {
            switch (s->segment) {
            /* Note how cases are right before RAC calls */
            case 1:
                RAC_GET(...);

            case 2:
                RAC_GET(...);
            ...
            }

            // End stuff
            ...
            return 0;
        } while (0);

        // EAGAIN stuff
        ...
        return AVERROR(EAGAIN);
    }


> 
> I give up here. There are further 4000+ lines to review. What an
> impressive patch.>> Just this:
> 
>> +static void transform_palette_reverse(FLIF16Context *ctx,
>> +                                     FLIF16TransformContext *t_ctx,
>> +                                     FLIF16PixelData *frame,
>> +                                     uint32_t stride_row,
>> +                                     uint32_t stride_col)
>> +{
>> +    int r, c;
>> +    int P;
> 
> ffmpeg doesn't use capitals in variable names.

Will fix. Will correct any similar errors as noted above.

> Cheers,
> Moritz

[1]: http://ffmpeg.org/pipermail/ffmpeg-devel/2020-March/258465.html
[2]: https://ffmpeg.org/pipermail/ffmpeg-devel/2020-February/257797.html
Anamitra Ghorui Aug. 27, 2020, 3:16 a.m. UTC | #3
(I apologise if this mail gets sent to the wrong thread and other
errors. I'm trying out a new mail client)

On Wed, 26 Aug 2020 16:31:46 +0000
"Anamitra Ghorui" <aghorui@teknik.io> wrote:

[...]

> I agree that the case statements between if statements and while
> statements are not very good looking, but this allows us to extract the
> function logic without having to set the, or look at the state
> management variables and statements. Trying to make do without the
> interleaving statements would cause us to set s->segment many times,
> set multiple switch statements etc.
> 
>     func(FLIF16XYZContext *s, ...)
>     {
>         // Init stuff
>         ...
>         
>         switch (s->segment) {
>         /* Note how cases are right before RAC calls */
>         case 1:
>             RAC_GET(...);
>             ...
>         case 2:
>             RAC_GET(...);
>             ...
>         ...
>         }
> 
>         // End stuff
>         ...
>         return 0;
> 
>     need more data:
>         ...
>         return AVERROR(EAGAIN);
>     }
> 
> I get your point about using do {} while (0) to break the sequence, it
> does make sense now to ditch the need_more_data entirely and hence free 
> it from having to using a specific goto label, however it introduces an 
> additional level of indentation. This is how majority of the decoder 
> logic and transform logic has been written, so it may make the code 
> look a bit bulkier. Should I do it anyway?
> 
>     func(FLIF16XYZContext *s, ...)
>     {
>         // Init stuff
>         ...
> 
>         do {
>             switch (s->segment) {
>             /* Note how cases are right before RAC calls */
>             case 1:
>                 RAC_GET(...);
> 
>             case 2:
>                 RAC_GET(...);
>             ...
>             }
> 
>             // End stuff
>             ...
>             return 0;
>         } while (0);
> 
>         // EAGAIN stuff
>         ...
>         return AVERROR(EAGAIN);
>     }
> 
> 

Wait a second, that's wrong. It should be like this instead:

    int main()
    {
        // Init Stuff
        ...
        switch (k) {
        case 0:
            RAC_GET(...)
            break;
        ...
        case N:
            RAC_GET(...)

        default:
            // Success end case
            // We don't want the control here if error case
            return 0;
        }

        // Error end case
        return AVERROR(EAGAIN);
    }

--
Regards,
Anamitra
Anamitra Ghorui Aug. 27, 2020, 3:57 a.m. UTC | #4
On Thu, 27 Aug 2020 08:46:00 +0530
Anamitra Ghorui <aghorui@teknik.io> wrote:

> (I apologise if this mail gets sent to the wrong thread and other
> errors. I'm trying out a new mail client)
> 
> On Wed, 26 Aug 2020 16:31:46 +0000
> "Anamitra Ghorui" <aghorui@teknik.io> wrote:
> 
> [...]
> 
> > I agree that the case statements between if statements and while
> > statements are not very good looking, but this allows us to extract the
> > function logic without having to set the, or look at the state
> > management variables and statements. Trying to make do without the
> > interleaving statements would cause us to set s->segment many times,
> > set multiple switch statements etc.
> > 
> >     func(FLIF16XYZContext *s, ...)
> >     {
> >         // Init stuff
> >         ...
> >         
> >         switch (s->segment) {
> >         /* Note how cases are right before RAC calls */
> >         case 1:
> >             RAC_GET(...);
> >             ...
> >         case 2:
> >             RAC_GET(...);
> >             ...
> >         ...
> >         }
> > 
> >         // End stuff
> >         ...
> >         return 0;
> > 
> >     need more data:
> >         ...
> >         return AVERROR(EAGAIN);
> >     }
> > 
> > I get your point about using do {} while (0) to break the sequence, it
> > does make sense now to ditch the need_more_data entirely and hence free 
> > it from having to using a specific goto label, however it introduces an 
> > additional level of indentation. This is how majority of the decoder 
> > logic and transform logic has been written, so it may make the code 
> > look a bit bulkier. Should I do it anyway?
> > 
> >     func(FLIF16XYZContext *s, ...)
> >     {
> >         // Init stuff
> >         ...
> > 
> >         do {
> >             switch (s->segment) {
> >             /* Note how cases are right before RAC calls */
> >             case 1:
> >                 RAC_GET(...);
> > 
> >             case 2:
> >                 RAC_GET(...);
> >             ...
> >             }
> > 
> >             // End stuff
> >             ...
> >             return 0;
> >         } while (0);
> > 
> >         // EAGAIN stuff
> >         ...
> >         return AVERROR(EAGAIN);
> >     }
> > 
> >   
> 
> Wait a second, that's wrong. It should be like this instead:
> 
>     int main()
>     {
>         // Init Stuff
>         ...
>         switch (k) {
>         case 0:
>             RAC_GET(...)
>             break;

Please ignore the break. It shouldn't be here. you might notice from
the function name I was trying to test the structure out with sample
code.

>         ...
>         case N:
>             RAC_GET(...)
> 
>         default:
>             // Success end case
>             // We don't want the control here if error case
>             return 0;
>         }
> 
>         // Error end case
>         return AVERROR(EAGAIN);
>     }

The above code may be the better option if we decide to remove the goto
label based approach, but I would like to hear your comments on it and
some of the other mentioned things first.

--
Regards,
Anamitra
Moritz Barsnick Aug. 27, 2020, 8:17 a.m. UTC | #5
On Wed, Aug 26, 2020 at 16:31:46 +0000, Anamitra Ghorui wrote:
> >> +uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
> >> +                               uint8_t *target)
> >> +{
> >> +    return ff_flif16_rac_get(rc, rc->range >> 1, target);
> >> +}
> >
> > If this is called often, you may want to mark it inline.
> >
>
> On Nicolas George's suggestion, I had dropped the inline keyword because
> the number of inline functions used may put stress on the memory cache.
> I think I should only inine the "higher" functions and not the "lower"
> functions like ff_flif16_rac_get in this case. Should I?

Ah, okay, I did read Nicolas's reviews, but I didn't recall this.
Please do go with his suggestions.

> I agree that the case statements between if statements and while
> statements are not very good looking, but this allows us to extract the
> function logic without having to set the, or look at the state
> management variables and statements. Trying to make do without the
> interleaving statements would cause us to set s->segment many times,
> set multiple switch statements etc.

I understand this justification. I just wanted to point out that the
code was not parsable to my brain, due to the macros (which I do
relaize have "goto"s in them, which is okay to me) and the "case"
labels in the middle of code blocks.

I suggest others comment on this as well.

> I get your point about using do {} while (0) to break the sequence, it

That was just a general remark regarding best practice. If it doesn't
fit into your above concept, then my comment doesn't apply.

Regards,
Moritz
Lynne Aug. 27, 2020, 5:49 p.m. UTC | #6
Aug 27, 2020, 10:17 by barsnick@gmx.net:

> On Wed, Aug 26, 2020 at 16:31:46 +0000, Anamitra Ghorui wrote:
>
>> >> +uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
>> >> +                               uint8_t *target)
>> >> +{
>> >> +    return ff_flif16_rac_get(rc, rc->range >> 1, target);
>> >> +}
>> >
>> > If this is called often, you may want to mark it inline.
>> >
>>
>> On Nicolas George's suggestion, I had dropped the inline keyword because
>> the number of inline functions used may put stress on the memory cache.
>> I think I should only inine the "higher" functions and not the "lower"
>> functions like ff_flif16_rac_get in this case. Should I?
>>
>
> Ah, okay, I did read Nicolas's reviews, but I didn't recall this.
> Please do go with his suggestions.
>

We always inline bitstream reading functions, and this one is no different.
Its really necessary for speed, and well worth the additional binary size.
There's no memory cache gains to be had here, not with the huge
several hundred instruction lookaheads modern CPUs have had for decades,
just function call overhead and stack spillage to be saved on.
If we could only put av_always_inline on a single type of function in decoders,
it would have to be in bitstream reading, so please use that here.
Nicolas George Aug. 27, 2020, 6:26 p.m. UTC | #7
Lynne (12020-08-27):
> We always inline bitstream reading functions, and this one is no different.
> Its really necessary for speed, and well worth the additional binary size.
> There's no memory cache gains to be had here, not with the huge
> several hundred instruction lookaheads modern CPUs have had for decades,
> just function call overhead and stack spillage to be saved on.
> If we could only put av_always_inline on a single type of function in decoders,
> it would have to be in bitstream reading, so please use that here.

The functions I asked not to inline were huge and calling each other,
growing exponentially, not the bitstream-reading ones.

Regards,
Nicolas George Aug. 27, 2020, 6:28 p.m. UTC | #8
Moritz Barsnick (12020-08-27):
> I understand this justification. I just wanted to point out that the
> code was not parsable to my brain, due to the macros (which I do
> relaize have "goto"s in them, which is okay to me) and the "case"
> labels in the middle of code blocks.
> 
> I suggest others comment on this as well.

I agree with you, it makes the code very hard to read. Maybe having a
real state engine, with clear dispatching and all variables in the
context takes a little more effort short-term, but in the long run it
will make things much easier to maintain.

Regards,
diff mbox series

Patch

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