Skip to content

Commit

Permalink
Update Sample Transform implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
y-guyon committed Jan 31, 2025
1 parent 83172c6 commit 275d085
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 54 deletions.
45 changes: 27 additions & 18 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,28 +179,37 @@ typedef enum avifSampleTransformTokenType
// Operands.
AVIF_SAMPLE_TRANSFORM_CONSTANT = 0,
AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX = 1,

// Operators. L is the left operand. R is the right operand if there are two operands.
AVIF_SAMPLE_TRANSFORM_NEGATE = 2, // S = -L
AVIF_SAMPLE_TRANSFORM_ABSOLUTE = 3, // S = |L|
AVIF_SAMPLE_TRANSFORM_SUM = 4, // S = L + R
AVIF_SAMPLE_TRANSFORM_DIFFERENCE = 5, // S = L - R
AVIF_SAMPLE_TRANSFORM_PRODUCT = 6, // S = L * R
AVIF_SAMPLE_TRANSFORM_DIVIDE = 7, // S = R==0 ? L : floor(L / R)
AVIF_SAMPLE_TRANSFORM_AND = 8, // S = L & R
AVIF_SAMPLE_TRANSFORM_OR = 9, // S = L | R
AVIF_SAMPLE_TRANSFORM_XOR = 10, // S = L ^ R
AVIF_SAMPLE_TRANSFORM_NOT = 11, // S = ~L
AVIF_SAMPLE_TRANSFORM_MSB = 12, // S = L<=0 ? 0 : floor(log2(L))
AVIF_SAMPLE_TRANSFORM_POW = 13, // S = L==0 ? 0 : pow(L, R)
AVIF_SAMPLE_TRANSFORM_MIN = 14, // S = L<=R ? L : R
AVIF_SAMPLE_TRANSFORM_MAX = 15, // S = L<=R ? R : L
AVIF_SAMPLE_TRANSFORM_RESERVED
AVIF_SAMPLE_TRANSFORM_FIRST_INPUT_IMAGE_ITEM_INDEX = 1,
AVIF_SAMPLE_TRANSFORM_LAST_INPUT_IMAGE_ITEM_INDEX = 32,

// Unary operators. L is the left operand.
AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR = 64,
AVIF_SAMPLE_TRANSFORM_NEGATION = 64, // S = -L
AVIF_SAMPLE_TRANSFORM_ABSOLUTE = 65, // S = |L|
AVIF_SAMPLE_TRANSFORM_NOT = 66, // S = ~L
AVIF_SAMPLE_TRANSFORM_BSR = 67, // S = L<=0 ? 0 : floor(log2(L))
AVIF_SAMPLE_TRANSFORM_LAST_UNARY_OPERATOR = 67,

// Binary operators. L is the left operand. R is the right operand.
AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR = 128,
AVIF_SAMPLE_TRANSFORM_SUM = 128, // S = L + R
AVIF_SAMPLE_TRANSFORM_DIFFERENCE = 129, // S = L - R
AVIF_SAMPLE_TRANSFORM_PRODUCT = 130, // S = L * R
AVIF_SAMPLE_TRANSFORM_QUOTIENT = 131, // S = R==0 ? L : floor(L / R)
AVIF_SAMPLE_TRANSFORM_AND = 132, // S = L & R
AVIF_SAMPLE_TRANSFORM_OR = 133, // S = L | R
AVIF_SAMPLE_TRANSFORM_XOR = 134, // S = L ^ R
AVIF_SAMPLE_TRANSFORM_POW = 135, // S = L==0 ? 0 : pow(L, R)
AVIF_SAMPLE_TRANSFORM_MIN = 136, // S = L<=R ? L : R
AVIF_SAMPLE_TRANSFORM_MAX = 137, // S = L<=R ? R : L
AVIF_SAMPLE_TRANSFORM_LAST_BINARY_OPERATOR = 137,

AVIF_SAMPLE_TRANSFORM_RESERVED = 138
} avifSampleTransformTokenType;

typedef struct avifSampleTransformToken
{
uint8_t type; // avifSampleTransformTokenType
avifSampleTransformTokenType type;
int32_t constant; // If type is AVIF_SAMPLE_TRANSFORM_CONSTANT.
// Only 32-bit (bit_depth=2) constants are supported.
uint8_t inputImageItemIndex; // If type is AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX. 1-based.
Expand Down
25 changes: 18 additions & 7 deletions src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -2115,14 +2115,24 @@ static avifResult avifParseSampleTransformTokens(avifROStream * s, avifSampleTra
avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(expression);
AVIF_CHECKERR(token != NULL, AVIF_RESULT_OUT_OF_MEMORY);

AVIF_CHECKERR(avifROStreamRead(s, &token->type, /*size=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) token;
if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT) {
uint8_t tokenValue = AVIF_SAMPLE_TRANSFORM_RESERVED;
AVIF_CHECKERR(avifROStreamRead(s, &tokenValue, /*size=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) token;
if (tokenValue == AVIF_SAMPLE_TRANSFORM_CONSTANT) {
token->type = AVIF_SAMPLE_TRANSFORM_CONSTANT;
// Two's complement representation is assumed here.
uint32_t constant;
AVIF_CHECKERR(avifROStreamReadU32(s, &constant), AVIF_RESULT_BMFF_PARSE_FAILED); // signed int(1<<(bit_depth+3)) constant;
token->constant = *(int32_t *)&constant; // maybe =(int32_t)constant; is enough
} else if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) {
AVIF_CHECKERR(avifROStreamRead(s, &token->inputImageItemIndex, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) input_image_item_index;
token->constant = (int32_t)constant;
} else if (tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_INPUT_IMAGE_ITEM_INDEX) {
AVIF_ASSERT_OR_RETURN(tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_INPUT_IMAGE_ITEM_INDEX);
token->type = AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX;
token->inputImageItemIndex = tokenValue;
} else if (tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR && tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_UNARY_OPERATOR) {
token->type = (avifSampleTransformTokenType)tokenValue; // unary operator
} else if (tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR && tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_BINARY_OPERATOR) {
token->type = (avifSampleTransformTokenType)tokenValue; // binary operator
} else {
token->type = AVIF_SAMPLE_TRANSFORM_RESERVED;
}
}
AVIF_CHECKERR(avifROStreamRemainingBytes(s) == 0, AVIF_RESULT_BMFF_PARSE_FAILED);
Expand All @@ -2138,8 +2148,9 @@ static avifResult avifParseSampleTransformImageBox(const uint8_t * raw,
{
BEGIN_STREAM(s, raw, rawLen, diag, "Box[sato]");

uint8_t version, bitDepth;
AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &version, /*bitCount=*/6), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(6) version = 0;
uint8_t version, reserved, bitDepth;
AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &version, /*bitCount=*/2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) version = 0;
AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) reserved;
AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &bitDepth, /*bitCount=*/2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) bit_depth;
AVIF_CHECKERR(version == 0, AVIF_RESULT_NOT_IMPLEMENTED);
AVIF_CHECKERR(bitDepth == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, AVIF_RESULT_NOT_IMPLEMENTED);
Expand Down
20 changes: 11 additions & 9 deletions src/sampletransform.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ avifBool avifSampleTransformExpressionIsValid(const avifSampleTransformExpressio
AVIF_CHECK(token->inputImageItemIndex != 0);
AVIF_CHECK(token->inputImageItemIndex <= numInputImageItems);
}
if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT || token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) {
if (token->type < AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR) {
// Likely an operand.
++stackSize;
} else if (token->type == AVIF_SAMPLE_TRANSFORM_NEGATE || token->type == AVIF_SAMPLE_TRANSFORM_ABSOLUTE ||
token->type == AVIF_SAMPLE_TRANSFORM_NOT || token->type == AVIF_SAMPLE_TRANSFORM_MSB) {
} else if (token->type < AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR) {
// Likely a unary operator.
AVIF_CHECK(stackSize >= 1);
// Pop one and push one.
} else {
// Likely a binary operator.
AVIF_CHECK(stackSize >= 2);
--stackSize; // Pop two and push one.
}
Expand Down Expand Up @@ -131,7 +133,7 @@ avifResult avifSampleTransformRecipeToExpression(avifSampleTransformRecipe recip
// The second image represents the 4 least significant bits of the reconstructed, bit-depth-extended output image.
AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 2));
AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 16));
AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_DIVIDE));
AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_QUOTIENT));
}
AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_SUM));
return AVIF_RESULT_OK;
Expand Down Expand Up @@ -198,13 +200,13 @@ static int32_t avifSampleTransformClamp32b(int64_t value)
static int32_t avifSampleTransformOperation32bOneOperand(int32_t operand, uint8_t operator)
{
switch (operator) {
case AVIF_SAMPLE_TRANSFORM_NEGATE:
case AVIF_SAMPLE_TRANSFORM_NEGATION:
return avifSampleTransformClamp32b(-(int64_t)operand);
case AVIF_SAMPLE_TRANSFORM_ABSOLUTE:
return operand >= 0 ? operand : avifSampleTransformClamp32b(-(int64_t)operand);
case AVIF_SAMPLE_TRANSFORM_NOT:
return ~operand;
case AVIF_SAMPLE_TRANSFORM_MSB: {
case AVIF_SAMPLE_TRANSFORM_BSR: {
if (operand <= 0) {
return 0;
}
Expand All @@ -230,7 +232,7 @@ static int32_t avifSampleTransformOperation32bTwoOperands(int32_t leftOperand, i
return avifSampleTransformClamp32b(leftOperand - rightOperand);
case AVIF_SAMPLE_TRANSFORM_PRODUCT:
return avifSampleTransformClamp32b(leftOperand * rightOperand);
case AVIF_SAMPLE_TRANSFORM_DIVIDE:
case AVIF_SAMPLE_TRANSFORM_QUOTIENT:
return rightOperand == 0 ? leftOperand : leftOperand / rightOperand;
case AVIF_SAMPLE_TRANSFORM_AND:
return leftOperand & rightOperand;
Expand Down Expand Up @@ -315,8 +317,8 @@ static avifResult avifImageApplyExpression32b(avifImage * dstImage,
const uint8_t * row = avifImagePlane(image, c) + avifImagePlaneRowBytes(image, c) * y;
AVIF_ASSERT_OR_RETURN(stackSize < stackCapacity);
stack[stackSize++] = avifImageUsesU16(image) ? ((const uint16_t *)row)[x] : row[x];
} else if (token->type == AVIF_SAMPLE_TRANSFORM_NEGATE || token->type == AVIF_SAMPLE_TRANSFORM_ABSOLUTE ||
token->type == AVIF_SAMPLE_TRANSFORM_NOT || token->type == AVIF_SAMPLE_TRANSFORM_MSB) {
} else if (token->type == AVIF_SAMPLE_TRANSFORM_NEGATION || token->type == AVIF_SAMPLE_TRANSFORM_ABSOLUTE ||
token->type == AVIF_SAMPLE_TRANSFORM_NOT || token->type == AVIF_SAMPLE_TRANSFORM_BSR) {
AVIF_ASSERT_OR_RETURN(stackSize >= 1);
stack[stackSize - 1] = avifSampleTransformOperation32bOneOperand(stack[stackSize - 1], token->type);
// Pop one and push one.
Expand Down
20 changes: 12 additions & 8 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,19 +1066,22 @@ static avifResult avifImageCopyAltImageMetadata(avifImage * altImageMetadata, co
#if defined(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM)
static avifResult avifEncoderWriteSampleTransformTokens(avifRWStream * s, const avifSampleTransformExpression * expression)
{
AVIF_ASSERT_OR_RETURN(expression->count <= 256);
AVIF_ASSERT_OR_RETURN(expression->count <= 255);
AVIF_CHECKRES(avifRWStreamWriteU8(s, (uint8_t)expression->count)); // unsigned int(8) token_count;

for (uint32_t t = 0; t < expression->count; ++t) {
const avifSampleTransformToken * token = &expression->tokens[t];
AVIF_CHECKRES(avifRWStreamWriteU8(s, token->type)); // unsigned int(8) token;

if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT) {
AVIF_CHECKRES(avifRWStreamWriteU8(s, token->type)); // unsigned int(8) token;
// TODO(yguyon): Verify two's complement representation is guaranteed here.
const uint32_t constant = *(const uint32_t *)&token->constant;
const uint32_t constant = (uint32_t)token->constant;
AVIF_CHECKRES(avifRWStreamWriteU32(s, constant)); // signed int(1<<(bit_depth+3)) constant;
} else if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) {
AVIF_CHECKRES(avifRWStreamWriteU8(s, token->inputImageItemIndex)); // unsigned int(8) input_image_item_index;
AVIF_CHECKRES(avifRWStreamWriteU8(s, token->inputImageItemIndex)); // unsigned int(8) token;
} else {
// Operator.
AVIF_CHECKRES(avifRWStreamWriteU8(s, token->type)); // unsigned int(8) token;
}
}
return AVIF_RESULT_OK;
Expand All @@ -1088,7 +1091,8 @@ static avifResult avifEncoderWriteSampleTransformPayload(avifEncoder * encoder,
{
avifRWStream s;
avifRWStreamStart(&s, data);
AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/6)); // unsigned int(6) version = 0;
AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/2)); // unsigned int(2) version = 0;
AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) reserved;
// AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32 is necessary because the two input images
// once combined use 16-bit unsigned values, but intermediate results are stored in signed integers.
AVIF_CHECKRES(avifRWStreamWriteBits(&s, AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, /*bitCount=*/2)); // unsigned int(2) bit_depth;
Expand Down Expand Up @@ -1450,15 +1454,15 @@ static avifResult avifEncoderCreateSatoImage(avifEncoder * encoder,
if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B) {
if (isBase) {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_DIVIDE, 256, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 256, planes));
} else {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_AND, 255, planes));
}
} else if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B) {
if (isBase) {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 12, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_DIVIDE, 16, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 16, planes));
} else {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_AND, 15, planes));
Expand Down Expand Up @@ -1487,7 +1491,7 @@ static avifResult avifEncoderCreateSatoImage(avifEncoder * encoder,
AVIF_RESULT_NOT_IMPLEMENTED);
if (isBase) {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 12, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_DIVIDE, 16, planes));
AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 16, planes));
} else {
AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes));
avifCodec * codec = NULL;
Expand Down
2 changes: 1 addition & 1 deletion tests/gtest/avif16bittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ TEST_P(SampleTransformTest, Avif16bit) {
{AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, 0,
/*inputImageItemIndex=*/1},
{AVIF_SAMPLE_TRANSFORM_CONSTANT, 1 << shift, 0},
{AVIF_SAMPLE_TRANSFORM_DIVIDE, 0, 0}};
{AVIF_SAMPLE_TRANSFORM_QUOTIENT, 0, 0}};
ASSERT_EQ(avifImageApplyOperations(
image_no_sato.get(), AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32,
/*numTokens=*/3, tokens, /*numInputImageItems=*/1,
Expand Down
22 changes: 11 additions & 11 deletions tests/gtest/avifsampletransformtest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AvifExpression : public avifSampleTransformExpression {
}
void AddOperator(avifSampleTransformTokenType op) {
avifSampleTransformToken& token = AddToken();
token.type = static_cast<uint8_t>(op);
token.type = op;
}

int32_t Apply() const {
Expand Down Expand Up @@ -158,10 +158,10 @@ TEST_P(SampleTransformOperationTest, Apply) {

INSTANTIATE_TEST_SUITE_P(
Operations, SampleTransformOperationTest,
testing::Values(Op(AVIF_SAMPLE_TRANSFORM_NEGATE, 1, 0),
Op(AVIF_SAMPLE_TRANSFORM_NEGATE, -1, 1),
Op(AVIF_SAMPLE_TRANSFORM_NEGATE, 0, 0),
Op(AVIF_SAMPLE_TRANSFORM_NEGATE, -256, 255),
testing::Values(Op(AVIF_SAMPLE_TRANSFORM_NEGATION, 1, 0),
Op(AVIF_SAMPLE_TRANSFORM_NEGATION, -1, 1),
Op(AVIF_SAMPLE_TRANSFORM_NEGATION, 0, 0),
Op(AVIF_SAMPLE_TRANSFORM_NEGATION, -256, 255),
Op(AVIF_SAMPLE_TRANSFORM_ABSOLUTE, 1, 1),
Op(AVIF_SAMPLE_TRANSFORM_ABSOLUTE, -1, 1),
Op(AVIF_SAMPLE_TRANSFORM_ABSOLUTE, 256, 255),
Expand All @@ -176,8 +176,8 @@ INSTANTIATE_TEST_SUITE_P(
Op(-1, AVIF_SAMPLE_TRANSFORM_DIFFERENCE, 1, 0),
Op(1, AVIF_SAMPLE_TRANSFORM_PRODUCT, 1, 1),
Op(2, AVIF_SAMPLE_TRANSFORM_PRODUCT, 3, 6),
Op(1, AVIF_SAMPLE_TRANSFORM_DIVIDE, 1, 1),
Op(2, AVIF_SAMPLE_TRANSFORM_DIVIDE, 3, 0),
Op(1, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 1, 1),
Op(2, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 3, 0),
Op(1, AVIF_SAMPLE_TRANSFORM_AND, 1, 1),
Op(1, AVIF_SAMPLE_TRANSFORM_AND, 2, 0),
Op(7, AVIF_SAMPLE_TRANSFORM_AND, 15, 7),
Expand All @@ -186,10 +186,10 @@ INSTANTIATE_TEST_SUITE_P(
Op(1, AVIF_SAMPLE_TRANSFORM_XOR, 3, 2),
Op(AVIF_SAMPLE_TRANSFORM_NOT, 254, 0),
Op(AVIF_SAMPLE_TRANSFORM_NOT, -1, 0),
Op(AVIF_SAMPLE_TRANSFORM_MSB, 0, 0),
Op(AVIF_SAMPLE_TRANSFORM_MSB, -1, 0),
Op(AVIF_SAMPLE_TRANSFORM_MSB, 61, 5),
Op(AVIF_SAMPLE_TRANSFORM_MSB,
Op(AVIF_SAMPLE_TRANSFORM_BSR, 0, 0),
Op(AVIF_SAMPLE_TRANSFORM_BSR, -1, 0),
Op(AVIF_SAMPLE_TRANSFORM_BSR, 61, 5),
Op(AVIF_SAMPLE_TRANSFORM_BSR,
std::numeric_limits<int32_t>::max(), 30),
Op(2, AVIF_SAMPLE_TRANSFORM_POW, 4, 16),
Op(4, AVIF_SAMPLE_TRANSFORM_POW, 2, 16),
Expand Down

0 comments on commit 275d085

Please sign in to comment.