Skip to content

Commit

Permalink
Allow different input for different layers (experimental, mostly reus…
Browse files Browse the repository at this point in the history
…ing grid functionality)
  • Loading branch information
tongyuantongyu committed Nov 19, 2021
1 parent 02bbb9b commit aaf1c25
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 36 deletions.
66 changes: 42 additions & 24 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,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 @@ -629,7 +629,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 @@ -655,7 +655,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 @@ -747,8 +747,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 @@ -879,7 +880,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 @@ -889,6 +892,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 @@ -1130,7 +1138,7 @@ int main(int argc, char * argv[])
}
}

if (input.filesCount > 1 && (layerConfig.layerCount > 1 || layerConfig.layerCountAlpha > 1)) {
if (gridDimsCount == 0 && input.filesCount > 1 && (layerConfig.layerCount > 1 || layerConfig.layerCountAlpha > 1)) {
fprintf(stderr, "Progressive animated AVIF currently not supported.\n");
returnCode = 1;
goto cleanup;
Expand Down Expand Up @@ -1319,17 +1327,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 @@ -1345,7 +1354,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, useSharpYUV);
if (nextInputFormat == AVIF_APP_FILE_FORMAT_UNKNOWN) {
Expand Down Expand Up @@ -1381,19 +1390,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 Down Expand Up @@ -1444,8 +1456,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
13 changes: 12 additions & 1 deletion include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ typedef enum avifResult
AVIF_RESULT_IO_ERROR,
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_NOT_IMPLEMENTED, // a requested code path is not (yet) implemented
AVIF_RESULT_INVALID_LAYERS,
} avifResult;

AVIF_API const char * avifResultToString(avifResult result);
Expand Down Expand Up @@ -1071,6 +1072,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 @@ -249,7 +249,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 @@ -93,6 +93,7 @@ const char * avifResultToString(avifResult result)
case AVIF_RESULT_WAITING_ON_IO: return "Waiting on IO";
case AVIF_RESULT_INVALID_ARGUMENT: return "Invalid argument";
case AVIF_RESULT_NOT_IMPLEMENTED: return "Not implemented";
case AVIF_RESULT_INVALID_LAYERS: return "Invalid layer image";
case AVIF_RESULT_UNKNOWN_ERROR:
default:
break;
Expand Down
2 changes: 1 addition & 1 deletion src/codec_aom.c
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
avifEncoder * encoder,
const avifImage * image,
avifBool alpha,
int layerIndex,
uint32_t layerIndex,
avifAddImageFlags addImageFlags,
avifCodecEncodeOutput * output)
{
Expand Down
2 changes: 1 addition & 1 deletion src/codec_rav1e.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ static avifResult rav1eCodecEncodeImage(avifCodec * codec,
avifEncoder * encoder,
const avifImage * image,
avifBool alpha,
int layerIndex,
uint32_t layerIndex,
uint32_t addImageFlags,
avifCodecEncodeOutput * output)
{
Expand Down
2 changes: 1 addition & 1 deletion src/codec_svt.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,
avifEncoder * encoder,
const avifImage * image,
avifBool alpha,
int layerIndex,
uint32_t layerIndex,
uint32_t addImageFlags,
avifCodecEncodeOutput * output)
{
Expand Down
46 changes: 39 additions & 7 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ static void avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t
static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
uint32_t gridCols,
uint32_t gridRows,
uint32_t layerCount,
const avifImage * const * cellImages,
uint64_t durationInTimescales,
avifAddImageFlags addImageFlags)
Expand All @@ -542,6 +543,7 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
// Validate images

const uint32_t cellCount = gridCols * gridRows;
const uint32_t imageCount = cellCount * layerCount;
if (cellCount == 0) {
return AVIF_RESULT_INVALID_ARGUMENT;
}
Expand Down Expand Up @@ -647,8 +649,8 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,

avifAlphaData alphaData;
encoder->data->alphaPresent = AVIF_FALSE;
for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) {
const avifImage * cellImage = cellImages[cellIndex];
for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
const avifImage * cellImage = cellImages[imageIndex];

if (cellImage->alphaPlane == NULL || cellImage->alphaRowBytes == 0) {
continue;
Expand Down Expand Up @@ -776,10 +778,11 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
if (item->codec) {
item->layerCount = item->alpha ? encoder->layerCountAlpha : encoder->layerCount;
item->layerCount = item->layerCount == 0 ? 1 : item->layerCount;
const avifImage * cellImage = cellImages[item->cellIndex];
for (int layerIndex = 0; layerIndex < item->layerCount; ++layerIndex) {
for (uint32_t layerIndex = 0; layerIndex < item->layerCount; ++layerIndex) {
const uint32_t index = (layerIndex > (layerCount - 1)) ? (layerCount - 1) : layerIndex;
const avifImage * layerImage = cellImages[item->cellIndex * layerCount + index];
avifResult encodeResult =
item->codec->encodeImage(item->codec, encoder, cellImage, item->alpha, layerIndex, addImageFlags, item->encodeOutput);
item->codec->encodeImage(item->codec, encoder, layerImage, item->alpha, layerIndex, addImageFlags, item->encodeOutput);
if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) {
encodeResult = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
}
Expand All @@ -798,7 +801,7 @@ static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags)
{
avifDiagnosticsClearError(&encoder->diag);
return avifEncoderAddImageInternal(encoder, 1, 1, &image, durationInTimescales, addImageFlags);
return avifEncoderAddImageInternal(encoder, 1, 1, 1, &image, durationInTimescales, addImageFlags);
}

avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
Expand All @@ -811,7 +814,36 @@ avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
if ((gridCols == 0) || (gridCols > 256) || (gridRows == 0) || (gridRows > 256)) {
return AVIF_RESULT_INVALID_IMAGE_GRID;
}
return avifEncoderAddImageInternal(encoder, gridCols, gridRows, cellImages, 1, addImageFlags | AVIF_ADD_IMAGE_FLAG_SINGLE); // only single image grids are supported
return avifEncoderAddImageInternal(encoder, gridCols, gridRows, 1, cellImages, 1, addImageFlags | AVIF_ADD_IMAGE_FLAG_SINGLE); // only single image grids are supported
}


avifResult avifEncoderAddImageProgressive(avifEncoder * encoder,
uint32_t layerCount,
const avifImage * const * layerImages,
avifAddImageFlags addImageFlags)
{
avifDiagnosticsClearError(&encoder->diag);
if ((layerCount == 0) || (layerCount > MAX_AV1_LAYER_COUNT)) {
return AVIF_RESULT_INVALID_LAYERS;
}
return avifEncoderAddImageInternal(encoder, 1, 1, layerCount, layerImages, 1, addImageFlags | AVIF_ADD_IMAGE_FLAG_SINGLE); // only single image grids are supported
}

avifResult avifEncoderAddImageProgressiveGrid(avifEncoder * encoder,
uint32_t gridCols,
uint32_t gridRows,
uint32_t layerCount,
const avifImage * const * layerImages,
avifAddImageFlags addImageFlags) {
avifDiagnosticsClearError(&encoder->diag);
if ((layerCount == 0) || (layerCount > MAX_AV1_LAYER_COUNT)) {
return AVIF_RESULT_INVALID_LAYERS;
}
if ((gridCols == 0) || (gridCols > 256) || (gridRows == 0) || (gridRows > 256)) {
return AVIF_RESULT_INVALID_IMAGE_GRID;
}
return avifEncoderAddImageInternal(encoder, gridCols, gridRows, layerCount, layerImages, 1, addImageFlags);
}

static size_t avifEncoderFindExistingChunk(avifRWStream * s, size_t mdatStartOffset, const uint8_t * data, size_t size)
Expand Down

0 comments on commit aaf1c25

Please sign in to comment.