From patchwork Fri Aug 28 18:45:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21965 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 155EB44B46C for ; Fri, 28 Aug 2020 21:45:57 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 025A268AD4D; Fri, 28 Aug 2020 21:45:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 135CC68A79B for ; Fri, 28 Aug 2020 21:45:48 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=tWNv+TqWj8vp09waZC1cUjQv1It4YqWsCeDFEKjDaas=; b=b28SkuGWfakWyrXe99LmGVjgv0fVkLgoO1EgWl1LBMwqI4hl4pyXv2UMHE/srEWFfYiiNrdVtchchbvLPUcphph/ajkAXOYg3hYxZa//m7LEFJu7ljqSlG2Q/CnMFC6u9WqgCl26LpvkuBe71wqlcSibaH6UBQ3ybMfYrTldrAbT6Qm/3UeP+d9KV6H3aKHtIv21f872doezWuI6XOrjgAYe58iHf0AlRUGNCGmm23N84Rd0GS5SIAqpxy Z+v4S6VRa96M7vBwFJJlE2AySxANlGTRNNPT1ISCdPyzS0usyaA4flnHL644+mDRuFpN9WPAM8p2hvPtDOtqpahAzUxg== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:46 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:26 +0530 Message-Id: <20200828184531.29647-2-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 1/6] avcodec: add FLIF decoding support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Co-authored-by: Anamitra Ghorui Co-authored-by: Kartik K Khullar Signed-off-by: Anamitra Ghorui --- libavcodec/Makefile | 3 + libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 + libavcodec/codec_id.h | 1 + libavcodec/flif16.c | 204 +++ libavcodec/flif16.h | 285 ++++ libavcodec/flif16_parser.c | 193 +++ libavcodec/flif16_rangecoder.c | 770 +++++++++ libavcodec/flif16_rangecoder.h | 422 +++++ libavcodec/flif16_transform.c | 2886 ++++++++++++++++++++++++++++++++ libavcodec/flif16_transform.h | 124 ++ libavcodec/flif16dec.c | 1762 +++++++++++++++++++ libavcodec/parsers.c | 1 + libavcodec/version.h | 2 +- 14 files changed, 6660 insertions(+), 1 deletion(-) create mode 100644 libavcodec/flif16.c create mode 100644 libavcodec/flif16.h create mode 100644 libavcodec/flif16_parser.c create mode 100644 libavcodec/flif16_rangecoder.c create mode 100644 libavcodec/flif16_rangecoder.h create mode 100644 libavcodec/flif16_transform.c create mode 100644 libavcodec/flif16_transform.h create mode 100644 libavcodec/flif16dec.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index ca2e8a2530..25a0d8345e 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..ba385aa059 --- /dev/null +++ b/libavcodec/flif16.c @@ -0,0 +1,204 @@ +/* + * FLIF16 Image Format Definitions + * Copyright (c) 2020 Anamitra Ghorui + * + * 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, + int32_t *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(int32_t)); + if (!frame->data[i]) + return AVERROR(ENOMEM); + break; + + case FLIF16_PLANEMODE_CONSTANT: + frame->data[i] = av_malloc(sizeof(int32_t)); + if (!frame->data[i]) + return AVERROR(ENOMEM); + ((int32_t *) frame->data[i])[0] = const_plane_value[i]; + break; + + case FLIF16_PLANEMODE_FILL: + frame->data[i] = av_malloc_array(s->width * s->height, sizeof(int32_t)); + if (!frame->data[i]) + return AVERROR(ENOMEM); + for (int k = 0; k < s->width * s->height; k++) + ((int32_t *) 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(uint32_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, + uint32_t curr_num_frames, + uint32_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, + uint8_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..8974585996 --- /dev/null +++ b/libavcodec/flif16.h @@ -0,0 +1,285 @@ +/* + * FLIF16 Image Format Definitions + * Copyright (c) 2020 Anamitra Ghorui + * + * 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 +#include + +#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 meta; ///< Size of a meta chunk + uint32_t num_frames; + + // Primary Header + uint16_t *framedelay; ///< Frame delay for each frame + uint32_t bpc; ///< 2 ^ Bits per channel - 1 + 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; + 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 *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(uint32_t num_frames); + +FLIF16PixelData *ff_flif16_frames_resize(FLIF16PixelData *frames, + uint32_t curr_num_frames, + uint32_t new_num_frames); + +void ff_flif16_frames_free(FLIF16PixelData **frames, uint32_t num_frames, + uint8_t num_planes, uint8_t lookback); + + + +/* + * All constant plane pixel settings 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]; +} + +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 + * + * 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 +#include + +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..ed8de300c3 --- /dev/null +++ b/libavcodec/flif16_rangecoder.c @@ -0,0 +1,770 @@ +/* + * Range coder for FLIF16 + * Copyright (c) 2020, Anamitra Ghorui + * + * 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; +} + +/* + * 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 av_always_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 + +/** + * 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 Coder + +static av_always_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; \ + } + + +/** + * Reads a near zero coded integer. + */ +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; +} + +/** + * Reads a generalized near zero coded integer. + */ +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 av_always_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 av_always_inline 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; + sbits += rc->log4k.table[bit ? c->chances[i] : 4096 - c->chances[i]]; + 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 av_always_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_chance(rc, ctx->data[type].chances[ctx->data[type].best], target); + ff_flif16_multiscale_chancetable_put(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 + + +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..2631c1ac01 --- /dev/null +++ b/libavcodec/flif16_rangecoder.h @@ -0,0 +1,422 @@ +/* + * Range coder for FLIF16 + * Copyright (c) 2020 Anamitra Ghorui + * + * 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 + + +#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 are non + * interchangable. + */ + +// #define MULTISCALE_CHANCES_ENABLED + +#define MULTISCALE_CHANCETABLE_DEFAULT_SIZE 6 +#define MULTISCALE_CHANCETABLE_DEFAULT_CUT 8 + +#define MANIAC_TREE_BASE_SIZE 160 +#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); + + +static av_always_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; +} + +static av_always_inline 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; +} + +static av_always_inline uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc, + uint8_t *target) +{ + return ff_flif16_rac_get(rc, rc->range >> 1, target); +} + +static av_always_inline 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); +} + +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); + +#endif + +int ff_flif16_maniac_read_int(FLIF16RangeCoder *rc, FLIF16MANIACContext *m, + int32_t *properties, uint8_t channel, + int min, int max, int *target); + +/** + * 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 av_always_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..46e990b17c --- /dev/null +++ b/libavcodec/flif16_transform.c @@ -0,0 +1,2886 @@ +/* + * Transforms for FLIF16 + * Copyright (c) 2020 Kartik K. Khullar + * + * 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 ctx_y; + FLIF16ChanceContext ctx_i; + FLIF16ChanceContext ctx_q; + 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 ctx_y; + FLIF16ChanceContext ctx_i; + FLIF16ChanceContext ctx_q; + FLIF16ChanceContext ctx_a; + 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 pixel_l[2], pixel_u[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_free(data->bounds); +} + +/* + * ChannelCompact + */ + +static FLIF16ColorVal ff_channelcompact_min(FLIF16RangesContext *ranges, int p) +{ + return 0; +} + +static FLIF16ColorVal ff_channelcompact_max(FLIF16RangesContext *src_ctx, int p) +{ + RangesPrivChannelcompact *data = src_ctx->priv_data; + return data->nb_colors[p]; +} + +static void ff_channelcompact_minmax(FLIF16RangesContext *r_ctx, int p, + FLIF16ColorVal *prev_planes, + FLIF16ColorVal *minv, FLIF16ColorVal *maxv) +{ + RangesPrivChannelcompact *data = r_ctx->priv_data; + *minv = 0; + *maxv = data->nb_colors[p]; +} + +/* + * YCoCg + */ + +static inline FLIF16ColorVal ff_get_max_y(int origmax4) +{ + return 4 * origmax4 - 1; +} + +static inline int ff_get_min_co(int origmax4, int yval) +{ + if (yval < origmax4 - 1) + return (-3) - (4 * yval); + else if (yval >= 3 * origmax4) + return 4 * (1 + yval - (4 * origmax4)); + else + return (-4) * origmax4 + 1; +} + +static inline int ff_get_max_co(int origmax4, int yval) +{ + if (yval < origmax4 - 1) + return 3 + 4 * yval; + else if (yval >= 3 * origmax4) + return 4 * origmax4 - 4 * (1 + yval - 3 * origmax4); + else + return 4 * origmax4 - 1; +} + +static inline int ff_get_min_cg(int origmax4, int yval, int coval) +{ + if (yval < origmax4 - 1) + return -(2 * yval + 1); + else if (yval >= 3 * origmax4) + return -(2 * (4 * origmax4 - 1 - yval) - ((1 + abs(coval)) / 2) * 2); + else { + return -FFMIN(2 * origmax4 - 1 + (yval - origmax4 + 1) * 2, + 2 * origmax4 + (3 * origmax4 - 1 - yval) * 2 - ((1 + abs(coval)) / 2)*2); + } +} + +static inline int ff_get_max_cg(int origmax4, int yval, int coval) +{ + if (yval < origmax4 - 1) + return 1 + 2 * yval - 2 * (abs(coval) / 2); + else if (yval >= 3 * origmax4) + return 2 * (4*origmax4 - 1 - yval); + else + return -FFMAX(- 4 * origmax4 + (1 + yval - 2 * origmax4) * 2, + - 2 * origmax4 - (yval - origmax4) * 2 - 1 + (abs(coval) / 2) * 2); +} + +static FLIF16ColorVal ff_ycocg_min(FLIF16RangesContext *r_ctx, int p) +{ + RangesPrivYCoCg *data = r_ctx->priv_data; + 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_free(data->r_ctx->priv_data); + av_free(data->bounds); + av_free(data->r_ctx); +} + +/* + * Palette + */ + +static FLIF16ColorVal ff_palette_min(FLIF16RangesContext *r_ctx, int p) +{ + RangesPrivPalette *data = r_ctx->priv_data; + if (p < 3) + return 0; + else + return ff_flif16_ranges_min(data->r_ctx, p); +} + +static FLIF16ColorVal ff_palette_max(FLIF16RangesContext *r_ctx, int p) +{ + RangesPrivPalette *data = r_ctx->priv_data; + if (p == 1) + return data->nb_colors-1; + else if (p < 3) + return 0; + else + return ff_flif16_ranges_max(data->r_ctx, p); +} + +static void ff_palette_minmax(FLIF16RangesContext *r_ctx, int p, + FLIF16ColorVal *prev_planes, + FLIF16ColorVal *minv, FLIF16ColorVal *maxv) +{ + RangesPrivPalette *data = r_ctx->priv_data; + if (p == FLIF16_PLANE_CO) { + *minv = 0; + *maxv = data->nb_colors-1; + } else if (p < FLIF16_PLANE_ALPHA) { + *minv = 0; + *maxv = 0; + } else + ff_flif16_ranges_minmax(data->r_ctx, p, prev_planes, minv, maxv); +} + +static void ff_palette_close(FLIF16RangesContext *r_ctx) +{ + RangesPrivPalette *data = r_ctx->priv_data; + const FLIF16Ranges *range = flif16_ranges[data->r_ctx->r_no]; + if (range->close) + range->close(data->r_ctx); + av_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_free(r_ctx->priv_data); + av_free(r_ctx); +} + +static inline 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 inline 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> 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: + 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_free(r_ctx); + return NULL; + } + data = r_ctx->priv_data; + data->bounds = trans_data->bounds; + ff_flif16_ranges_close(src_ctx); + } 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->ctx_y); + ff_flif16_chancecontext_init(&data->ctx_i); + ff_flif16_chancecontext_init(&data->ctx_q); + 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->ctx_y, 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->ctx_i, + 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->ctx_q, 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->ctx_y, 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->ctx_i, 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->ctx_q, 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->ctx_y); + ff_flif16_chancecontext_init(&data->ctx_i); + ff_flif16_chancecontext_init(&data->ctx_q); + ff_flif16_chancecontext_init(&data->ctx_a); + 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->ctx_a, 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->ctx_y, + 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->ctx_i, + 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->ctx_q, 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->ctx_a, + 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->ctx_y, 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->ctx_i, 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->ctx_q, 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 pixel_l[2], pixel_u[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)) { + pixel_l[0] = cb->min0; + pixel_u[0] = cb->min0 + 1 - 1; + pixel_l[1] = cb->min1; + pixel_u[1] = cb->min1 + 4 - 1; + for (int i = 0; i < cb->bucket2_size; i++) { + pixel_l[1] = cb->min1; + pixel_u[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 = pixel_l[1]; c <= pixel_u[1]; c++) { + if (!ff_remove_color(ff_bucket_buckets2(cb, 1, pixel_l), c)) + return NULL; + if (!ff_remove_color(ff_bucket_buckets2(cb, 1, pixel_u), c)) + return NULL; + } + } + pixel_l[1] += 4; + pixel_u[1] += 4; + } + pixel_l[0] += 1; + pixel_u[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 *pixel_l, FLIF16ColorVal *pixel_u) +{ + 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, pixel_l, pixel_u)) { + return 1; + } + } + cb->smin = 0; + cb->smax = 0; + cb->i = 1; + + case 1: + transform_colorbuckets_minmax(src_ctx, plane, + pixel_l, pixel_u, + &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->pixel_l, data->pixel_u); + if (ret <= 0) + return AVERROR(EAGAIN); + data->pixel_l[0] = cb->min0; + data->pixel_u[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->pixel_l, data->pixel_u); + if (ret <= 0) + return AVERROR(EAGAIN); + data->pixel_l[0] += 1; + data->pixel_u[0] += 1; + } + data->j = 0; + + if (ff_flif16_ranges_min(src_ctx, 2) < ff_flif16_ranges_max(src_ctx, 2)) { + data->pixel_l[0] = cb->min0; + data->pixel_u[0] = cb->min0 + 1 - 1; + data->pixel_l[1] = cb->min1; + data->pixel_u[1] = cb->min1 + 4 - 1; + data->i = 2; + } else + data->i = 3; + } + + switch (data->i) { + for (; data->j < cb->bucket2_size; data->j++) { + data->pixel_l[1] = cb->min1; + data->pixel_u[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->pixel_l, data->pixel_u); + if (ret <= 0) + return AVERROR(EAGAIN); + data->pixel_l[1] += 4; + data->pixel_u[1] += 4; + } + data->k = 0; + data->pixel_l[0] += 1; + data->pixel_u[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->pixel_l, data->pixel_u); + 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_free(ranges); + return NULL; + } + av_assert0(data->max_lookback < frame_count); + data->was_greyscale = (src_ctx->num_planes < 2); + data->was_flat = (src_ctx->num_planes < 4); + + data->orig_num_planes = ctx->num_planes; + ctx->num_planes = 5; + + lookback = frame_count - 1; + if (lookback > data->max_lookback) + lookback = data->max_lookback; + + ranges->r_no = FLIF16_RANGES_FRAMELOOKBACK; + ranges->num_planes = 5; + ranges->priv_data = rdata; + + rdata->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, + .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, + .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, + .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..f04b23cabc --- /dev/null +++ b/libavcodec/flif16_transform.h @@ -0,0 +1,124 @@ +/* + * Transforms for FLIF16 + * Copyright (c) 2020 Kartik K. Khullar + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Transforms for FLIF16. + */ + +#ifndef AVCODEC_FLIF16_TRANSFORM_H +#define AVCODEC_FLIF16_TRANSFORM_H + +#include "avcodec.h" +#include "libavutil/common.h" +#include "flif16.h" + +typedef enum FLIF16RangesTypes { + FLIF16_RANGES_CHANNELCOMPACT, + FLIF16_RANGES_YCOCG, + FLIF16_RANGES_PERMUTEPLANES, + FLIF16_RANGES_PERMUTEPLANESSUBTRACT, + FLIF16_RANGES_BOUNDS, + FLIF16_RANGES_STATIC, + FLIF16_RANGES_PALETTEALPHA, + FLIF16_RANGES_PALETTE, + FLIF16_RANGES_COLORBUCKETS, + FLIF16_RANGES_FRAMELOOKBACK +} FLIF16RangesTypes; + +typedef enum FLIF16TransformsTypes { + FLIF16_TRANSFORM_CHANNELCOMPACT, + FLIF16_TRANSFORM_YCOCG, + FLIF16_TRANSFORM_RESERVED1, + FLIF16_TRANSFORM_PERMUTEPLANES, + FLIF16_TRANSFORM_BOUNDS, + FLIF16_TRANSFORM_PALETTEALPHA, + FLIF16_TRANSFORM_PALETTE, + FLIF16_TRANSFORM_COLORBUCKETS, + FLIF16_TRANSFORM_RESERVED2, + FLIF16_TRANSFORM_RESERVED3, + FLIF16_TRANSFORM_DUPLICATEFRAME, + FLIF16_TRANSFORM_FRAMESHAPE, + FLIF16_TRANSFORM_FRAMELOOKBACK, +} FLIF16TransformsTypes; + +extern const FLIF16Ranges *flif16_ranges[14]; +extern const FLIF16Transform *flif16_transforms[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..7388516873 --- /dev/null +++ b/libavcodec/flif16dec.c @@ -0,0 +1,1762 @@ +/* + * FLIF16 Decoder + * Copyright (c) 2020 Anamitra Ghorui + * + * 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 meta; ///< Size of a meta chunk + uint32_t num_frames; + + // Primary Header + uint16_t *framedelay; ///< Frame delay for each frame + uint32_t bpc; ///< 2 ^ Bits per channel - 1 + 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; + } + } + + 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; + int32_t const_plane_value[MAX_PLANES]; + uint8_t temp; + + switch (s->segment) { + while (1) { + case 0: + RAC_GET(&s->rc, NULL, 0, 0, &temp, FLIF16_RAC_BIT); + if (!temp) + break; + s->segment++; + + case 1: + RAC_GET(&s->rc, NULL, 0, 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++; + } // End While + + 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, \ From patchwork Fri Aug 28 18:45:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21967 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id C5F9C44B46C for ; Fri, 28 Aug 2020 21:45:58 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B158868AE44; Fri, 28 Aug 2020 21:45:58 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5D864689767 for ; Fri, 28 Aug 2020 21:45:49 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=xfSbUtBpDQMUVzX7ZkkFisBo78x9mFqAOeLD4eNgvkc=; b=bDi3HeArfD+KANjAZyEkTDK2nFIP+BwIxffL6gnwiANjj6Xu3zyTY5603qqh95FwokGQ6JcKzBYhIgfqFFYWEAFUNkrZiwqbx3LNbLKN8yOjfCEh8C0NaNvZem5UL6QT9QZkIdXj/yxdnzCq4FI+/sjf+tf9NeRb0bYaiBJykVNd3heXsMRC8SWVv68et0gZD2zOgDAyOvPpSufGaq08HUpfnx/C6uRvYaxrAzJW4X9EFGT7g/S/NG0QBv UX+5jqni7PPIl/QyLdOWoiZCgmy7p4gJSznYkiDtntj6/B0B/Am3FS9Ro7iS8nmR+9jyWxVKYFt2s6RRBKJfOerOCljA== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:48 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:27 +0530 Message-Id: <20200828184531.29647-3-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 2/6] avformat: add FLIF demuxing support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Anamitra Ghorui --- configure | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/flifdec.c | 445 +++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 4 +- 5 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 libavformat/flifdec.c diff --git a/configure b/configure index 564072053b..b99f8ed935 100755 --- a/configure +++ b/configure @@ -3307,6 +3307,7 @@ fifo_muxer_deps="threads" flac_demuxer_select="flac_parser" flv_muxer_select="aac_adtstoasc_bsf" gxf_muxer_select="pcm_rechunk_bsf" +flif_demuxer_select="zlib exif" hds_muxer_select="flv_muxer" hls_muxer_select="mpegts_muxer" hls_muxer_suggest="gcrypt openssl" diff --git a/libavformat/Makefile b/libavformat/Makefile index cbb33fe37c..a105122526 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -192,6 +192,7 @@ OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ vorbiscomment.o OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o +OBJS-$(CONFIG_FLIF_DEMUXER) += flifdec.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 0aa9dd7198..d003889b6f 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -150,6 +150,7 @@ extern AVOutputFormat ff_fits_muxer; extern AVInputFormat ff_flac_demuxer; extern AVOutputFormat ff_flac_muxer; extern AVInputFormat ff_flic_demuxer; +extern AVInputFormat ff_flif_demuxer; extern AVInputFormat ff_flv_demuxer; extern AVOutputFormat ff_flv_muxer; extern AVInputFormat ff_live_flv_demuxer; diff --git a/libavformat/flifdec.c b/libavformat/flifdec.c new file mode 100644 index 0000000000..a7272075fd --- /dev/null +++ b/libavformat/flifdec.c @@ -0,0 +1,445 @@ +/* + * FLIF demuxer + * Copyright (c) 2020 Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FLIF demuxer. + */ + +#include "avformat.h" +#include "libavutil/common.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "libavcodec/exif.h" + +#include "libavcodec/flif16.h" +#include "libavcodec/flif16_rangecoder.h" + +#include "config.h" + +#if CONFIG_ZLIB +# include +#endif + +/* + * FLIF's reference encoder currently encodes metadata as a raw DEFLATE stream + * (RFC 1951). In order to decode a raw deflate stream using Zlib, inflateInit2 + * must be used with windowBits being between -8 .. -15. + */ +#define ZLIB_WINDOW_BITS -15 +#define BUF_SIZE 4096 + +typedef struct FLIFDemuxContext { +#if CONFIG_ZLIB + z_stream stream; + uint8_t active; +#endif +} FLIFDemuxContext; + + +#if CONFIG_ZLIB +static int flif_inflate(FLIFDemuxContext *s, uint8_t *buf, int buf_size, + uint8_t **out_buf, int *out_buf_size) +{ + int ret; + z_stream *stream = &s->stream; + + if (!s->active) { + s->active = 1; + stream->zalloc = Z_NULL; + stream->zfree = Z_NULL; + stream->opaque = Z_NULL; + stream->avail_in = 0; + stream->next_in = Z_NULL; + ret = inflateInit2(stream, ZLIB_WINDOW_BITS); + + if (ret != Z_OK) + return ret; + + *out_buf_size = buf_size; + *out_buf = av_realloc_f(*out_buf, *out_buf_size, 1); + if (!*out_buf) + return AVERROR(ENOMEM); + } + + stream->next_in = buf; + stream->avail_in = buf_size; + + do { + while (stream->total_out >= (*out_buf_size - 1)) { + *out_buf = av_realloc_f(*out_buf, (*out_buf_size) * 2, 1); + if (!*out_buf) + return AVERROR(ENOMEM); + *out_buf_size *= 2; + } + + stream->next_out = *out_buf + stream->total_out; + stream->avail_out = *out_buf_size - stream->total_out - 1; + + ret = inflate(stream, Z_PARTIAL_FLUSH); + + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + (void)inflateEnd(stream); + return AVERROR_INVALIDDATA; + case Z_MEM_ERROR: + (void)inflateEnd(stream); + return AVERROR(ENOMEM); + } + } while (stream->avail_in > 0); + + if (ret == Z_STREAM_END) { + s->active = 0; + (*out_buf)[stream->total_out] = '\0'; + (void) inflateEnd(stream); + } else + ret = AVERROR(EAGAIN); + + return ret; // Return Z_BUF_ERROR/EAGAIN as long as input is incomplete. +} +#endif + +#if CONFIG_ZLIB && CONFIG_EXIF +static int flif_read_exif(void *logctx, uint8_t *buf, int buf_size, AVDictionary **d) +{ + uint8_t le; + uint32_t temp; + int ret; + GetByteContext gb; + + // Read exif header + if (memcmp("Exif", buf, 4)) + return AVERROR_INVALIDDATA; + + buf += 6; + + // Figure out endianness + if (buf[0] == 'M' && buf[1] == 'M') + le = 0; + else if (buf[0] == 'I' && buf[1] == 'I') + le = 1; + else + return AVERROR_INVALIDDATA; + + buf += 2; + + bytestream2_init(&gb, buf, buf_size - 8); + temp = ff_tget_short(&gb, le); + + // Check TIFF marker + if (temp != 0x002A) + return AVERROR_INVALIDDATA; + + buf += 2; + + if (le) + temp = bytestream2_get_le32(&gb); + else + temp = bytestream2_get_be32(&gb); + + // Subtract read bytes, then skip + bytestream2_skip(&gb, temp - 8); + + ret = ff_exif_decode_ifd(logctx, &gb, le, 0, d); + + return ret; +} +#endif + +static int flif16_probe(const AVProbeData *p) +{ + uint32_t vlist[3] = {0}; + unsigned int count = 0, pos = 0; + + // Magic Number + if (memcmp(p->buf, flif16_header, 4)) { + return 0; + } + + for (int i = 0; i < 2 + (((p->buf[4] >> 4) > 4) ? 1 : 0); i++) { + while (p->buf[5 + pos] > 127) { + if (!(count--)) { + return 0; + } + VARINT_APPEND(vlist[i], p->buf[5 + pos]); + ++pos; + } + VARINT_APPEND(vlist[i], p->buf[5 + pos]); + count = 0; + } + + if (!((vlist[0] + 1) && (vlist[1] + 1))) + return 0; + + if (((p->buf[4] >> 4) > 4) && !(vlist[2] + 2)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int flif16_read_header(AVFormatContext *s) +{ +#if CONFIG_ZLIB + FLIFDemuxContext *dc = s->priv_data; +#endif + GetByteContext gb; + FLIF16RangeCoder rc = (FLIF16RangeCoder) {0}; + AVIOContext *pb = s->pb; + AVStream *st; + + int64_t duration = 0; + uint32_t vlist[3] = {0}; + uint32_t flag, animated, temp; + uint32_t bpc = 0; + uint32_t metadata_size = 0; +#if CONFIG_ZLIB + int out_buf_size = 0; + int buf_size = 0; +#endif + unsigned int count = 4; + int ret; + int format; + int segment = 0, i = 0; + uint32_t num_frames; + uint8_t num_planes; + + // Suppress unused variable compiler warning if Zlib is not present. +#if !CONFIG_ZLIB + av_unused uint8_t tag[5] = {0}; +#else + uint8_t tag[5] = {0}; +#endif + + uint8_t buf[BUF_SIZE]; + uint8_t *out_buf = NULL; + uint8_t loops = 0; + +#if !CONFIG_ZLIB + av_log(s, AV_LOG_WARNING, "ffmpeg has not been compiled with Zlib. Metadata may not be decoded.\n"); +#endif + + // Magic Number + if (avio_rl32(pb) != (*((uint32_t *) flif16_header))) { + av_log(s, AV_LOG_ERROR, "bad magic number\n"); + return AVERROR_INVALIDDATA; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + flag = avio_r8(pb); + animated = (flag >> 4) > 4; + duration = !animated; + bpc = avio_r8(pb); // Bytes per channel + + num_planes = flag & 0x0F; + + for (int i = 0; i < (2 + animated); i++) { + while ((temp = avio_r8(pb)) > 127) { + if (!(count--)) + return AVERROR_INVALIDDATA; + VARINT_APPEND(vlist[i], temp); + } + VARINT_APPEND(vlist[i], temp); + count = 4; + } + + vlist[0]++; + vlist[1]++; + + if (animated) + vlist[2] += 2; + else + vlist[2] = 1; + + num_frames = vlist[2]; + + while ((temp = avio_r8(pb))) { + // Get metadata identifier + tag[0] = temp; + for(int i = 1; i <= 3; i++) + tag[i] = avio_r8(pb); + + // Read varint + while ((temp = avio_r8(pb)) > 127) { + if (!(count--)) + return AVERROR_INVALIDDATA; + VARINT_APPEND(metadata_size, temp); + } + VARINT_APPEND(metadata_size, temp); + count = 4; + +#if CONFIG_ZLIB + + /* + * Decompression Routines + * There are 3 supported metadata chunks currently in FLIF: eXmp, eXif, + * and iCCp. Currently, iCCp color profiles are not handled. + */ + + if (*((uint32_t *) tag) == MKTAG('i','C','C','P')) { + goto metadata_skip; + } + + while (metadata_size > 0) { + if ((buf_size = avio_read_partial(pb, buf, FFMIN(BUF_SIZE, metadata_size))) < 0) + return buf_size; + metadata_size -= buf_size; + if((ret = flif_inflate(dc, buf, buf_size, &out_buf, &out_buf_size)) < 0 && + ret != AVERROR(EAGAIN)) { + if (ret == AVERROR(ENOMEM)) + return ret; + av_log(s, AV_LOG_ERROR, "could not decode metadata segment: %s\n", tag); + goto metadata_skip; + } + } + + switch (*((uint32_t *) tag)) { + case MKTAG('e', 'X', 'i','f'): +#if CONFIG_EXIF + ret = flif_read_exif(s, out_buf, out_buf_size, &s->metadata); + if (ret < 0) + av_log(s, AV_LOG_WARNING, "metadata may be corrupted\n"); +#endif + break; + + default: + av_dict_set(&s->metadata, tag, out_buf, 0); + break; + } +#else + avio_skip(pb, metadata_size); +#endif + + continue; + +#if CONFIG_ZLIB +metadata_skip: + avio_skip(pb, metadata_size); +#endif + } + + av_freep(&out_buf); + + do { + if ((ret = avio_read_partial(pb, buf, BUF_SIZE)) < 0) + return ret; + bytestream2_init(&gb, buf, ret); + } while (ff_flif16_rac_init(&rc, &gb) < 0); + + while (1) { + switch (segment) { + case 0: + if (bpc == '0') { + bpc = 0; + for (; i < num_planes; i++) { + RAC_GET(&rc, NULL, 1, 15, &temp, FLIF16_RAC_UNI_INT8); + bpc = FFMAX(bpc, (1 << temp) - 1); + } + i = 0; + } else + bpc = (bpc == '1') ? 255 : 65535; + if (num_frames < 2) + goto end; + segment++; + + case 1: + if (num_planes > 3) { + RAC_GET(&rc, NULL, 0, 1, &temp, FLIF16_RAC_UNI_INT8); + } + segment++; + + case 2: + if (num_frames > 1) { + RAC_GET(&rc, NULL, 0, 100, &loops, FLIF16_RAC_UNI_INT8); + } + if (!loops) + loops = 1; + segment++; + + case 3: + if (num_frames > 1) { + for (; i < num_frames; i++) { + temp = 0; + RAC_GET(&rc, NULL, 0, 60000, &(temp), FLIF16_RAC_UNI_INT16); + duration += temp; + } + i = 0; + } else + duration = 1; + goto end; + } + +need_more_data: + if ((ret = avio_read_partial(pb, buf, BUF_SIZE)) < 0) + return ret; + bytestream2_init(&gb, buf, ret); + } + +end: + if (bpc > 65535) { + av_log(s, AV_LOG_ERROR, "depth per channel greater than 16 bits not supported\n"); + return AVERROR_PATCHWELCOME; + } + + format = flif16_out_frame_type[FFMIN(num_planes, 4)][bpc > 255]; + + // The minimum possible delay in a FLIF16 image is 1 millisecond. + // Therefore time base is 10^-3, i.e. 1/1000 + avpriv_set_pts_info(st, 64, 1, 1000); + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_FLIF16; + st->codecpar->width = vlist[0]; + st->codecpar->height = vlist[1]; + st->codecpar->format = format; + st->duration = duration * loops; + st->start_time = 0; + st->nb_frames = vlist[2]; + st->need_parsing = 1; + + // Jump to start because flif16 decoder needs header data too + if (avio_seek(pb, 0, SEEK_SET) != 0) + return AVERROR(EIO); + return 0; +} + + +static int flif16_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + int ret; + ret = av_get_packet(pb, pkt, avio_size(pb)); + return ret; +} + +AVInputFormat ff_flif_demuxer = { + .name = "flif", + .long_name = NULL_IF_CONFIG_SMALL("Free Lossless Image Format (FLIF)"), + .priv_data_size = sizeof(FLIFDemuxContext), + .extensions = "flif", + .flags = AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS, + .read_probe = flif16_probe, + .read_header = flif16_read_header, + .read_packet = flif16_read_packet, +}; diff --git a/libavformat/version.h b/libavformat/version.h index 88876aec79..146db09d1b 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,8 +32,8 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 51 -#define LIBAVFORMAT_VERSION_MICRO 101 +#define LIBAVFORMAT_VERSION_MINOR 52 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ From patchwork Fri Aug 28 18:45:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21963 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 592B144B46C for ; Fri, 28 Aug 2020 21:45:53 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 4082568AC37; Fri, 28 Aug 2020 21:45:53 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D110E6880CA for ; Fri, 28 Aug 2020 21:45:50 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=ZLlKLXZkx5Fvsmu63jAGMZ6eWCadYTfVnewqCuOg224=; b=aGUi32fCGkZRl3D5a8wnSqypW6aqbEMj1loIRuY/RGi1oZG1Xq7TKE1FY+uj2KKoBwPsa8q/3PGVvbLiEXsWN+JxkmGfV0of9h3maMmrtACrXSgsdu+a1mA6fBCdN1AbZAMb9KzSaOfDW7NApcgs7zFwrdVFaFkLPq5PZQRuaU2zLqYOH8hnfnI3eqgHZo2wq6scD9YJDK8C2vIcI7sZVCmYSCDil3i/74/Uly6tFoB7Pxd0bQfh17A/VI rmOLKofJN2ChRUcK60/h+8QCuZOqwi6ZCt/uopQPnPJWv0lTIh1DDcNKFkNdzu2FH8R5PKpqdMyvgyAqgn8Aoi4uPvMw== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:50 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:28 +0530 Message-Id: <20200828184531.29647-4-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 3/6] changelog: add entries for FLIF X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Anamitra Ghorui --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 7467e73306..b55f322963 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,8 @@ version : - Argonaut Games ASF muxer - AV1 Low overhead bitstream format demuxer - RPZA video encoder +- FLIF16 decoder +- FLIF demuxer version 4.3: From patchwork Fri Aug 28 18:45:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21964 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 5BD0C44B46C for ; Fri, 28 Aug 2020 21:45:56 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3E62F68A636; Fri, 28 Aug 2020 21:45:56 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 411DC68A9D5 for ; Fri, 28 Aug 2020 21:45:52 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=pM80HT/dn4BPs7OJ7A1fgPRJLOykugh1tr6ZKSnHxQk=; b=SB6DAs99llXb1qF+/cE1Ik00zHJfVXfO43ExAUks/wb7TCaOvqz05+h0zCH7N4g7Ub9nW0rLyBgCIPd0s0/JgQGMwoo504+HG9WwFfYkzvYNXkTJaYbzlB1a+RP3HvVOs5P4Rh3noymL2AR776iZGk8RxEzpnCYfePFIam0/Q7WpRorfB6PQOqJywgm0jO7mENczSp/m934h5A11yW7IzwBQwRJeXye0IXaMCpZ1EbOBFvPN9rtEiqIEbe GlYlAVtEhOGLye8i5PYBz9BMVjg5zTFsUn5N6461KWsa2jR0+lilJvCKag88lmWdW3qcZQuQhHhc/BlLxq5qM52qv+VA== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:51 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:29 +0530 Message-Id: <20200828184531.29647-5-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 4/6] doc/general: add entry for FLIF X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Anamitra Ghorui --- doc/general.texi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/general.texi b/doc/general.texi index d618565347..e9ae535c34 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -904,6 +904,8 @@ following image formats are supported: @item Flash Screen Video v2 @tab X @tab X @item Flash Video (FLV) @tab X @tab X @tab Sorenson H.263 used in Flash +@item FLIF (Free Lossless Image Format @tab @tab X + @tab Precursor to JPEG XL and FUIF @item FM Screen Capture Codec @tab @tab X @item Forward Uncompressed @tab @tab X @item Fraps @tab @tab X From patchwork Fri Aug 28 18:45:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21966 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id C01F544B46C for ; Fri, 28 Aug 2020 21:45:57 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id AC7CA68AEBC; Fri, 28 Aug 2020 21:45:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6047968ACEC for ; Fri, 28 Aug 2020 21:45:53 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=7uoLy+b37mxMKgOB31lRN1lfMey+DBXW0hJdq7xr16A=; b=lLovf/w5e5M1rGpcX8sSuuYhuIxvWuaC9cwQkvw4NCWP9oIfAXOZKPASQU6o6igHpDU6T4HGyX4EtbEYV1qxWkQl6sJhDBM4+BDQGJASa6zrHtEARAn7/7hsCUZcSe1OM7l+megYV9ZmvI4Yv5aa6BBkAobg0PhubhX1KX17P/1ZkOSeE2m908/4bkXPnWSbCX7BKM3X7AycMxTi3T4TPs2uAOXeThva+EE6XSntxJUmFZlJe3gNo7OIL0 NOpFMmQ8kokOn6+PHPiSLaUrXAvcqphaqBm4wf4T/sQfdHyXHdacCAo+dPMv5Y0xa12++VQYLGH0+tC8txo8mKHzk5YA== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:52 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:30 +0530 Message-Id: <20200828184531.29647-6-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 5/6] doc/decoders: add entry for FLIF X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Anamitra Ghorui --- doc/decoders.texi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/decoders.texi b/doc/decoders.texi index 9005714e3c..77e93ab80a 100644 --- a/doc/decoders.texi +++ b/doc/decoders.texi @@ -86,6 +86,14 @@ AVS2-P2/IEEE1857.4 video decoder wrapper. This decoder allows libavcodec to decode AVS2 streams with davs2 library. +@section flif16 + +FLIF16 non-animated/animated image decoder. + +This decoder allows libavcodec to decode non-animated and animated FLIF images +as per the FLIF16 Specification. Interlaced images are always decoded to the +highest quality (zoom) level. + @c man end VIDEO DECODERS @chapter Audio Decoders From patchwork Fri Aug 28 18:45:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 21968 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 8A1AA44B46C for ; Fri, 28 Aug 2020 21:45:59 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7717068AF66; Fri, 28 Aug 2020 21:45:59 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9E67468AD4D for ; Fri, 28 Aug 2020 21:45:54 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:MIME-Version:Content-Transfer-Encoding:In-Reply-To:References; bh=FOYAGUrBjdvrHTnkMxo/e0wViwSMc4DO40wi2wh/SdE=; b=foeTiwL9FIEJLcpamltfO3boonYdVji+r2SL3FIP9FQRjKTGrex3mKCpk2Vqh26adlutjl23G860Oi3Yt67Ab30aNxsjvqa7+i0aGKjIbXqvWK1ZwBiC/EFX0H4QpLhvjfQpEUfwu7i5X0lXS9P/S9Ubl2ZXwAtenBNXh2R5GDyGdptfEqIda6Ta33hcuV4EOqi96hzDJI6cnsb2y1WedwR5FWMK0yK11Y8pY3H1y87QfkQ9+usQxVIUHg vWGnOIEngV+YZzDVerEpCYKRCwA5sD0v81tdmWo5BHnkS16nGJtkGmXlW79G0OrFL9rYs3s4aLy0HR+/ehk3k6bcP8Cg== Received: from localhost.localdomain (Unknown [103.82.156.1]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 28 Aug 2020 11:45:54 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Sat, 29 Aug 2020 00:15:31 +0530 Message-Id: <20200828184531.29647-7-aghorui@teknik.io> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200828184531.29647-1-aghorui@teknik.io> References: <20200828184531.29647-1-aghorui@teknik.io> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v7 6/6] doc/demuxers: add entry for FLIF X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Anamitra Ghorui --- doc/demuxers.texi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 3c15ab9eee..69b2ce5025 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -243,6 +243,15 @@ which streams to actually receive. Each stream mirrors the @code{id} and @code{bandwidth} properties from the @code{} as metadata keys named "id" and "variant_bitrate" respectively. +@section flif + +FLIF non-animated/animated image demuxer. + +This decoder allows libavformat to demux non-animated and animated FLIF images +as per the current FLIF16 Specification. +The entirety of the file is attempted to be read at once. No timestamps are set +for the format due to its nature. + @section flv, live_flv Adobe Flash Video Format demuxer.