Fix Sample Transform pow operator (#2806)
Add test.
diff --git a/src/sampletransform.c b/src/sampletransform.c
index 7fdfb05..eaf6da5 100644
--- a/src/sampletransform.c
+++ b/src/sampletransform.c
@@ -244,24 +244,24 @@
if (leftOperand == 0 || leftOperand == 1) {
return leftOperand;
}
- const uint32_t exponent = rightOperand > 0 ? (uint32_t)rightOperand : (uint32_t) - (int64_t)rightOperand;
- if (exponent == 0) {
+ if (rightOperand == 0) {
return 1;
}
- if (exponent == 1) {
+ if (rightOperand == 1) {
return leftOperand;
}
if (leftOperand == -1) {
- return (exponent % 2 == 0) ? 1 : -1;
+ return (rightOperand % 2 == 0) ? 1 : -1;
}
-
+ if (rightOperand < 0) {
+ // L^R is in ]0:1[ here, so truncating it always gives 0.
+ return 0;
+ }
int64_t result = leftOperand;
- for (uint32_t i = 1; i < exponent; ++i) {
+ for (int32_t i = 1; i < rightOperand; ++i) {
result *= leftOperand;
- if (result <= INT32_MIN) {
- return INT32_MIN;
- } else if (result >= INT32_MAX) {
- return INT32_MAX;
+ if (result < INT32_MIN || result > INT32_MAX) {
+ return (leftOperand > 0 || rightOperand % 2 == 0) ? INT32_MAX : INT32_MIN;
}
}
return (int32_t)result;
diff --git a/tests/gtest/avifsampletransformtest.cc b/tests/gtest/avifsampletransformtest.cc
index 1305380..3ee140a 100644
--- a/tests/gtest/avifsampletransformtest.cc
+++ b/tests/gtest/avifsampletransformtest.cc
@@ -64,6 +64,14 @@
}
};
+int32_t Eval(int32_t a, int32_t b, avifSampleTransformTokenType op) {
+ AvifExpression e;
+ e.AddConstant(a);
+ e.AddConstant(b);
+ e.AddOperator(op);
+ return e.Apply();
+}
+
//------------------------------------------------------------------------------
TEST(SampleTransformTest, NoExpression) {
@@ -129,6 +137,49 @@
EXPECT_EQ(e.Apply(), 255);
}
+TEST(SampleTransformTest, Pow) {
+ // Results are clamped to uint8_t.
+
+ EXPECT_EQ(Eval(-2, INT32_MIN, AVIF_SAMPLE_TRANSFORM_POW), 0);
+ EXPECT_EQ(Eval(-2, -3, AVIF_SAMPLE_TRANSFORM_POW), 0);
+ EXPECT_EQ(Eval(-2, -2, AVIF_SAMPLE_TRANSFORM_POW), 0);
+ EXPECT_EQ(Eval(-2, -1, AVIF_SAMPLE_TRANSFORM_POW), 0);
+ EXPECT_EQ(Eval(-2, 0, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-2, 1, AVIF_SAMPLE_TRANSFORM_POW), 0); // -2 clamped
+ EXPECT_EQ(Eval(-2, 2, AVIF_SAMPLE_TRANSFORM_POW), 4);
+ EXPECT_EQ(Eval(-2, 3, AVIF_SAMPLE_TRANSFORM_POW), 0); // -8 clamped
+ EXPECT_EQ(Eval(-2, INT32_MAX - 1, AVIF_SAMPLE_TRANSFORM_POW),
+ 255); // INT32_MAX clamped
+ EXPECT_EQ(Eval(-2, INT32_MAX, AVIF_SAMPLE_TRANSFORM_POW),
+ 0); // INT32_MIN clamped
+
+ EXPECT_EQ(Eval(-1, INT32_MIN, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-1, -3, AVIF_SAMPLE_TRANSFORM_POW), 0); // -1 clamped
+ EXPECT_EQ(Eval(-1, -2, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-1, -1, AVIF_SAMPLE_TRANSFORM_POW), 0); // -1 clamped
+ EXPECT_EQ(Eval(-1, 0, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-1, 1, AVIF_SAMPLE_TRANSFORM_POW), 0); // -1 clamped
+ EXPECT_EQ(Eval(-1, 2, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-1, 3, AVIF_SAMPLE_TRANSFORM_POW), 0); // -1 clamped
+ EXPECT_EQ(Eval(-1, INT32_MAX - 1, AVIF_SAMPLE_TRANSFORM_POW), 1);
+ EXPECT_EQ(Eval(-1, INT32_MAX, AVIF_SAMPLE_TRANSFORM_POW), 0); // -1 clamped
+
+ for (int32_t v : {0, 1}) {
+ EXPECT_EQ(Eval(v, INT32_MIN, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, -2, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, -1, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, 0, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, 1, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, 2, AVIF_SAMPLE_TRANSFORM_POW), v);
+ EXPECT_EQ(Eval(v, INT32_MAX, AVIF_SAMPLE_TRANSFORM_POW), v);
+ }
+
+ EXPECT_EQ(Eval(-(1 << 16), 3, AVIF_SAMPLE_TRANSFORM_POW),
+ 0); // INT32_MIN clamped
+ EXPECT_EQ(Eval((1 << 16), 3, AVIF_SAMPLE_TRANSFORM_POW),
+ 255); // INT32_MAX clamped
+}
+
//------------------------------------------------------------------------------
struct Op {