Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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
84 changes: 51 additions & 33 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ static void syntax(void)
AVIF_QUANTIZER_LOSSLESS);
printf(" --tilerowslog2 R : Set log2 of number of tile rows (0-6, default: 0)\n");
printf(" --tilecolslog2 C : Set log2 of number of tile columns (0-6, default: 0)\n");
printf(" -g,--grid MxN : Encode a single-image grid AVIF with M cols & N rows. Either supply MxN identical W/H/D images, or a single\n");
printf(" image that can be evenly split into the MxN grid and follow AVIF grid image restrictions. The grid will adopt\n");
printf(" the color profile of the first image supplied.\n");
printf(" -g,--grid MxN(xL) : Encode a single-image grid AVIF with M cols & N rows & L layers. Either supply MxNxL identical W/H/D images,\n");
printf(" or L identical W/H/D images that each can be evenly split into the MxN grid and follow AVIF grid image restrictions.\n");
printf(" The grid will adopt the color profile of the first image supplied.\n");
printf(" -s,--speed S : Encoder speed (%d-%d, slowest-fastest, 'default' or 'd' for codec internal defaults. default speed: 6)\n",
AVIF_SPEED_SLOWEST,
AVIF_SPEED_FASTEST);
Expand Down Expand Up @@ -270,8 +270,8 @@ static avifBool convertCropToClap(uint32_t srcW, uint32_t srcH, avifPixelFormat

struct avifEncoderLayerConfig
{
uint8_t layerCount; // Image layers for color sub image; 0 to disable layer image (default).
uint8_t layerCountAlpha; // Image layers for alpha sub image; 0 to disable layer image (default).
int extraLayerCount; // Image layers for color sub image; 0 to disable layer image (default).
int extraLayerCountAlpha; // Image layers for alpha sub image; 0 to disable layer image (default).
avifLayerConfig layers[MAX_AV1_LAYER_COUNT];
avifLayerConfig layersAlpha[MAX_AV1_LAYER_COUNT];
};
Expand Down Expand Up @@ -322,7 +322,7 @@ enum avifProgressiveConfigValueType

static avifBool avifParseProgressiveConfig(struct avifEncoderLayerConfig * config, const char * arg)
{
uint8_t * currLayerCount = &config->layerCount;
int * currLayerCount = &config->extraLayerCount;
avifLayerConfig * currLayers = config->layers;
uint8_t currLayer = 0;

Expand Down Expand Up @@ -487,11 +487,11 @@ static avifBool avifParseProgressiveConfig(struct avifEncoderLayerConfig * confi
++currLayer;
}

*currLayerCount = currLayer;
*currLayerCount = currLayer - 1;
if (*arg == ';') {
CHECK(currLayers == config->layers, "too many sub image configurations");
currLayers = config->layersAlpha;
currLayerCount = &config->layerCountAlpha;
currLayerCount = &config->extraLayerCountAlpha;

if (*(arg + 1) == '\0') {
goto finish;
Expand All @@ -503,7 +503,7 @@ static avifBool avifParseProgressiveConfig(struct avifEncoderLayerConfig * confi
// reached \0
if (currLayers == config->layers) {
memcpy(config->layersAlpha, config->layers, sizeof(config->layers));
config->layerCountAlpha = config->layerCount;
config->extraLayerCountAlpha = config->extraLayerCount;
}

goto finish;
Expand Down Expand Up @@ -628,7 +628,7 @@ static avifBool readEntireFile(const char * filename, avifRWData * raw)
return AVIF_TRUE;
}

static avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gridCols, uint32_t gridRows, avifImage ** gridCells)
static avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gridCols, uint32_t gridRows, uint32_t layerCount, avifImage ** gridCells)
{
if ((gridSplitImage->width % gridCols) != 0) {
fprintf(stderr, "ERROR: Can't split image width (%u) evenly into %u columns.\n", gridSplitImage->width, gridCols);
Expand All @@ -654,7 +654,7 @@ static avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gr
for (uint32_t gridX = 0; gridX < gridCols; ++gridX) {
uint32_t gridIndex = gridX + (gridY * gridCols);
avifImage * cellImage = avifImageCreateEmpty();
gridCells[gridIndex] = cellImage;
gridCells[gridIndex * layerCount] = cellImage;

avifImageCopy(cellImage, gridSplitImage, 0);
cellImage->width = cellWidth;
Expand Down Expand Up @@ -746,8 +746,9 @@ int main(int argc, char * argv[])
avifBool cicpExplicitlySet = AVIF_FALSE;
avifBool premultiplyAlpha = AVIF_FALSE;
int gridDimsCount = 0;
uint32_t gridDims[8]; // only the first two are used
uint32_t gridDims[8]; // only the first two or three are used
uint32_t gridCellCount = 0;
uint32_t gridCellLayerCount = 0;
avifImage ** gridCells = NULL;
avifImage * gridSplitImage = NULL; // used for cleanup tracking
memset(gridDims, 0, sizeof(gridDims));
Expand Down Expand Up @@ -877,7 +878,9 @@ int main(int argc, char * argv[])
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--grid")) {
NEXTARG();
gridDimsCount = parseU32List(gridDims, arg);
if (gridDimsCount != 2) {
if (gridDimsCount == 2) {
gridDims[2] = 1;
} else if (gridDimsCount != 3) {
fprintf(stderr, "ERROR: Invalid grid dims: %s\n", arg);
returnCode = 1;
goto cleanup;
Expand All @@ -887,6 +890,11 @@ int main(int argc, char * argv[])
returnCode = 1;
goto cleanup;
}
if ((gridDims[2] == 0 || gridDims[2] > MAX_AV1_LAYER_COUNT)) {
fprintf(stderr, "ERROR: Invalid layer count (valid layer range [1-4]): %s\n", arg);
returnCode = 1;
goto cleanup;
}
} else if (!strcmp(arg, "--cicp") || !strcmp(arg, "--nclx")) {
NEXTARG();
int cicp[3];
Expand Down Expand Up @@ -1111,7 +1119,7 @@ int main(int argc, char * argv[])
}
}

if (input.filesCount > 1 && (layerConfig.layerCount > 1 || layerConfig.layerCountAlpha > 1)) {
if (gridDimsCount == 0 && input.filesCount > 1 && (layerConfig.extraLayerCount > 0 || layerConfig.extraLayerCountAlpha > 0)) {
fprintf(stderr, "Progressive animated AVIF currently not supported.\n");
returnCode = 1;
goto cleanup;
Expand Down Expand Up @@ -1300,17 +1308,18 @@ int main(int argc, char * argv[])
gridCellCount = gridDims[0] * gridDims[1];
printf("Preparing to encode a %ux%u grid (%u cells)...\n", gridDims[0], gridDims[1], gridCellCount);

gridCells = calloc(gridCellCount, sizeof(avifImage *));
gridCellLayerCount = gridCellCount * gridDims[2];
gridCells = calloc(gridCellLayerCount, sizeof(avifImage *));
gridCells[0] = image; // take ownership of image

uint32_t gridCellIndex = 0;
uint32_t gridCellLayerIndex = 0;
avifInputFile * nextFile;
while ((nextFile = avifInputGetNextFile(&input)) != NULL) {
if (!gridCellIndex) {
if (!gridCellLayerIndex) {
printf("Loading additional cells for grid image (%u cells)...\n", gridCellCount);
}
++gridCellIndex;
if (gridCellIndex >= gridCellCount) {
++gridCellLayerIndex;
if (gridCellLayerIndex >= gridCellLayerCount) {
// We have enough, warn and continue
fprintf(stderr,
"WARNING: [--grid] More than %u images were supplied for this %ux%u grid. The rest will be ignored.\n",
Expand All @@ -1326,7 +1335,7 @@ int main(int argc, char * argv[])
cellImage->matrixCoefficients = image->matrixCoefficients;
cellImage->yuvRange = image->yuvRange;
cellImage->alphaPremultiplied = image->alphaPremultiplied;
gridCells[gridCellIndex] = cellImage;
gridCells[gridCellLayerIndex] = cellImage;

avifAppFileFormat nextInputFormat = avifInputReadImage(&input, cellImage, NULL, NULL);
if (nextInputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) {
Expand Down Expand Up @@ -1362,19 +1371,22 @@ int main(int argc, char * argv[])
}
}

if (gridCellIndex == 0) {
if (gridCellLayerIndex == gridDims[2] - 1 && (gridDims[0] != 1 || gridDims[1] != 1)) {
printf("Single image input for a grid image. Attempting to split into %u cells...\n", gridCellCount);
gridSplitImage = image;
gridCells[0] = NULL;

if (!avifImageSplitGrid(gridSplitImage, gridDims[0], gridDims[1], gridCells)) {
returnCode = 1;
goto cleanup;
for (uint32_t layerIndex = 0; layerIndex < gridDims[2]; ++layerIndex) {
gridSplitImage = gridCells[layerIndex];
gridCells[layerIndex] = NULL;
if (!avifImageSplitGrid(gridSplitImage, gridDims[0], gridDims[1], gridDims[2], gridCells + layerIndex)) {
returnCode = 1;
goto cleanup;
}
}
gridCellIndex = gridCellCount - 1;

gridCellLayerIndex = gridCellLayerCount - 1;
}

if (gridCellIndex != gridCellCount - 1) {
if (gridCellLayerIndex != gridCellLayerCount - 1) {
fprintf(stderr, "ERROR: Not enough input files for grid image! (expecting %u, or a single image to be split)\n", gridCellCount);
returnCode = 1;
goto cleanup;
Expand All @@ -1386,7 +1398,7 @@ int main(int argc, char * argv[])
lossyHint = " (Lossless)";
}
printf("AVIF to be written:%s\n", lossyHint);
avifBool progressive = layerConfig.layerCount > 1 || layerConfig.layerCountAlpha > 1;
avifBool progressive = layerConfig.extraLayerCount > 0 || layerConfig.extraLayerCountAlpha > 0;
avifImageDump(gridCells ? gridCells[0] : image,
gridDims[0],
gridDims[1],
Expand All @@ -1412,8 +1424,8 @@ int main(int argc, char * argv[])
encoder->minQuantizerAlpha = minQuantizerAlpha;
encoder->maxQuantizerAlpha = maxQuantizerAlpha;

encoder->layerCount = layerConfig.layerCount;
encoder->layerCountAlpha = layerConfig.layerCountAlpha;
encoder->extraLayerCount = layerConfig.extraLayerCount;
encoder->extraLayerCountAlpha = layerConfig.extraLayerCountAlpha;
memcpy(encoder->layers, layerConfig.layers, sizeof(encoder->layers));
memcpy(encoder->layersAlpha, layerConfig.layersAlpha, sizeof(encoder->layersAlpha));

Expand All @@ -1425,8 +1437,14 @@ int main(int argc, char * argv[])
encoder->keyframeInterval = keyframeInterval;

if (gridDimsCount > 0) {
avifResult addImageResult =
avifEncoderAddImageGrid(encoder, gridDims[0], gridDims[1], (const avifImage * const *)gridCells, AVIF_ADD_IMAGE_FLAG_SINGLE);
avifResult addImageResult;
if (gridDims[2] > 1) {
addImageResult =
avifEncoderAddImageProgressiveGrid(encoder, gridDims[0], gridDims[1], gridDims[2], (const avifImage * const *)gridCells, AVIF_ADD_IMAGE_FLAG_SINGLE);
} else {
addImageResult =
avifEncoderAddImageGrid(encoder, gridDims[0], gridDims[1], (const avifImage * const *)gridCells, AVIF_ADD_IMAGE_FLAG_SINGLE);
}
if (addImageResult != AVIF_RESULT_OK) {
fprintf(stderr, "ERROR: Failed to encode image grid: %s\n", avifResultToString(addImageResult));
returnCode = 1;
Expand Down
17 changes: 14 additions & 3 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,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 @@ -1041,8 +1042,8 @@ typedef struct avifEncoder
int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
uint64_t timescale; // timescale of the media (Hz)

uint8_t layerCount; // Image layers for color sub image; 0 to disable layer image (default).
uint8_t layerCountAlpha; // Image layers for alpha sub image; 0 to disable layer image (default).
int extraLayerCount; // Extra layers for color sub image; 0 to disable layer image (default).
int extraLayerCountAlpha; // Extra layers for alpha sub image; 0 to disable layer image (default).
avifLayerConfig layers[MAX_AV1_LAYER_COUNT];
avifLayerConfig layersAlpha[MAX_AV1_LAYER_COUNT];

Expand Down Expand Up @@ -1094,6 +1095,16 @@ AVIF_API avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
uint32_t gridRows,
const avifImage * const * cellImages,
avifAddImageFlags addImageFlags);
avifResult avifEncoderAddImageProgressive(avifEncoder * encoder,
uint32_t layerCount,
const avifImage * const * layerImages,
avifAddImageFlags addImageFlags);
avifResult avifEncoderAddImageProgressiveGrid(avifEncoder * encoder,
uint32_t gridCols,
uint32_t gridRows,
uint32_t layerCount,
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
2 changes: 1 addition & 1 deletion include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ typedef avifResult (*avifCodecEncodeImageFunc)(struct avifCodec * codec,
avifEncoder * encoder,
const avifImage * image,
avifBool alpha,
int layerIndex,
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