Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Progressive AVIF encoding #761

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ typedef int avifBool;
#define AVIF_SPEED_SLOWEST 0
#define AVIF_SPEED_FASTEST 10

#define MAX_AV1_LAYER_COUNT 4

typedef enum avifPlanesFlag
{
AVIF_PLANES_YUV = (1 << 0),
Expand Down Expand Up @@ -146,7 +148,8 @@ typedef enum avifResult
AVIF_RESULT_WAITING_ON_IO, // similar to EAGAIN/EWOULDBLOCK, this means the avifIO doesn't have necessary data available yet
AVIF_RESULT_INVALID_ARGUMENT, // an argument passed into this function is invalid
AVIF_RESULT_NOT_IMPLEMENTED, // a requested code path is not (yet) implemented
AVIF_RESULT_OUT_OF_MEMORY
AVIF_RESULT_OUT_OF_MEMORY,
AVIF_RESULT_INVALID_LAYERS
} avifResult;

AVIF_API const char * avifResultToString(avifResult result);
Expand Down Expand Up @@ -782,9 +785,11 @@ typedef enum avifProgressiveState
// for an image sequence.
AVIF_PROGRESSIVE_STATE_UNAVAILABLE = 0,

// The current AVIF/Source offers a progressive image, but avifDecoder.allowProgressive is not
// enabled, so it will behave as if the image was not progressive and will simply decode the
// best version of this item.
// For decoder, this means the current AVIF/Source offers a progressive image, but
// avifDecoder.allowProgressive is not enabled, so it will behave as if the image was not
// progressive and will simply decode the best version of this item.
// For encoder, this means at least one of color and alpha image has multiple layers and
// indicates this is a progressive image.
AVIF_PROGRESSIVE_STATE_AVAILABLE,

// The current AVIF/Source offers a progressive image, and avifDecoder.allowProgressive is true.
Expand Down Expand Up @@ -993,6 +998,20 @@ AVIF_API avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, ui
struct avifEncoderData;
struct avifCodecSpecificOptions;

typedef struct avifScalingMode
{
uint64_t numerator;
uint64_t denominator;
} avifScalingMode;

typedef struct avifLayerConfig
{
int minQuantizer;
int maxQuantizer;
avifScalingMode horizontalMode;
avifScalingMode verticalMode;
} avifLayerConfig;

// Notes:
// * If avifEncoderWrite() returns AVIF_RESULT_OK, output must be freed with avifRWDataFree()
// * If (maxThreads < 2), multithreading is disabled
Expand Down Expand Up @@ -1021,6 +1040,14 @@ typedef struct avifEncoder
int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
uint64_t timescale; // timescale of the media (Hz)

// Layers (used by progressive rendering)
// * Note: libavif currently can only properly decode images without alpha,
// or images whose extraLayerCount == extraLayerCountAlpha, if progressive decode is enabled.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this warning.

What happens exactly with alpha and extraLayerCount != extraLayerCountAlpha? Does libavif return an error or does it proceed with the decoding but in some wrong way? I cannot tell from your comment.

int extraLayerCount; // Extra color layers; 0 for regular single-layer color image (default).
int extraLayerCountAlpha; // Extra alpha layers; 0 for regular single-layer alpha image (default).
avifLayerConfig layers[MAX_AV1_LAYER_COUNT];
avifLayerConfig layersAlpha[MAX_AV1_LAYER_COUNT];

// stats from the most recent write
avifIOStats ioStats;

Expand All @@ -1043,9 +1070,9 @@ typedef enum avifAddImageFlag
// Force this frame to be a keyframe (sync frame).
AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME = (1 << 0),

// Use this flag when encoding a single image. Signals "still_picture" to AV1 encoders, which
// tweaks various compression rules. This is enabled automatically when using the
// avifEncoderWrite() single-image encode path.
// Use this flag when encoding a single frame, single layer image.
// Signals "still_picture" to AV1 encoders, which tweaks various compression rules.
// This is enabled automatically when using the avifEncoderWrite() single-image encode path.
AVIF_ADD_IMAGE_FLAG_SINGLE = (1 << 1)
} avifAddImageFlag;
typedef uint32_t avifAddImageFlags;
Expand All @@ -1058,17 +1085,22 @@ typedef uint32_t avifAddImageFlags;
// * avifEncoderAddImage() ... [repeatedly; at least once]
// OR
// * avifEncoderAddImageGrid() [exactly once, AVIF_ADD_IMAGE_FLAG_SINGLE is assumed]
// OR
// * avifEncoderAddImageProgressive() [exactly once, AVIF_ADD_IMAGE_FLAG_SINGLE is assumed]
// * avifEncoderFinish()
// * avifEncoderDestroy()
//

// durationInTimescales is ignored if AVIF_ADD_IMAGE_FLAG_SINGLE is set in addImageFlags.
AVIF_API avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags);
// cellImages should have gridCols * gridRows * (max(encoder->extraLayerCount, encoder->extraLayerCountAlpha) + 1) elements.
AVIF_API avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
uint32_t gridCols,
uint32_t gridRows,
const avifImage * const * cellImages,
avifAddImageFlags addImageFlags);
// layerImages should have max(encoder->extraLayerCount, encoder->extraLayerCountAlpha) + 1 elements.
avifResult avifEncoderAddImageProgressive(avifEncoder * encoder, const avifImage * const * layerImages, avifAddImageFlags addImageFlags);
AVIF_API avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output);

// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs. These
Expand Down
1 change: 1 addition & 0 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ typedef avifResult (*avifCodecEncodeImageFunc)(struct avifCodec * codec,
avifEncoder * encoder,
const avifImage * image,
avifBool alpha,
uint32_t layerIndex,
avifAddImageFlags addImageFlags,
avifCodecEncodeOutput * output);
typedef avifBool (*avifCodecEncodeFinishFunc)(struct avifCodec * codec, avifCodecEncodeOutput * output);
Expand Down
1 change: 1 addition & 0 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const char * avifResultToString(avifResult result)
case AVIF_RESULT_INVALID_ARGUMENT: return "Invalid argument";
case AVIF_RESULT_NOT_IMPLEMENTED: return "Not implemented";
case AVIF_RESULT_OUT_OF_MEMORY: return "Out of memory";
case AVIF_RESULT_INVALID_LAYERS: return "Invalid layer image";
case AVIF_RESULT_UNKNOWN_ERROR:
default:
break;
Expand Down
Loading