| 282 | AVIF_ARRAY_DECLARE(avifSampleTransformStack32b, int32_t, elements); |
| 283 | |
| 284 | static avifResult avifImageApplyExpression32b(avifImage * dstImage, |
| 285 | const avifSampleTransformExpression * expression, |
| 286 | const avifImage * inputImageItems[], |
| 287 | avifPlanesFlags planes, |
| 288 | int32_t * stack, |
| 289 | uint32_t stackCapacity) |
| 290 | { |
| 291 | // This slow path could be avoided by recognizing the recipe thanks to avifSampleTransformExpressionToRecipe() |
| 292 | // and having a dedicated optimized implementation for each recipe. |
| 293 | |
| 294 | const int32_t minValue = 0; |
| 295 | const int32_t maxValue = (1 << dstImage->depth) - 1; |
| 296 | |
| 297 | const avifBool skipColor = !(planes & AVIF_PLANES_YUV); |
| 298 | const avifBool skipAlpha = !(planes & AVIF_PLANES_A); |
| 299 | for (int c = AVIF_CHAN_Y; c <= AVIF_CHAN_A; ++c) { |
| 300 | const avifBool alpha = c == AVIF_CHAN_A; |
| 301 | if ((skipColor && !alpha) || (skipAlpha && alpha)) { |
| 302 | continue; |
| 303 | } |
| 304 | |
| 305 | const uint32_t planeWidth = avifImagePlaneWidth(dstImage, c); |
| 306 | const uint32_t planeHeight = avifImagePlaneHeight(dstImage, c); |
| 307 | for (uint32_t y = 0; y < planeHeight; ++y) { |
| 308 | for (uint32_t x = 0; x < planeWidth; ++x) { |
| 309 | uint32_t stackSize = 0; |
| 310 | for (uint32_t t = 0; t < expression->count; ++t) { |
| 311 | const avifSampleTransformToken * token = &expression->tokens[t]; |
| 312 | if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT) { |
| 313 | AVIF_ASSERT_OR_RETURN(stackSize < stackCapacity); |
| 314 | stack[stackSize++] = token->constant; |
| 315 | } else if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) { |
| 316 | const avifImage * image = inputImageItems[token->inputImageItemIndex - 1]; // 1-based |
| 317 | const uint8_t * row = avifImagePlane(image, c); |
| 318 | AVIF_ASSERT_OR_RETURN(row != NULL); |
| 319 | row += (size_t)avifImagePlaneRowBytes(image, c) * y; |
| 320 | AVIF_ASSERT_OR_RETURN(stackSize < stackCapacity); |
| 321 | stack[stackSize++] = avifImageUsesU16(image) ? ((const uint16_t *)row)[x] : row[x]; |
| 322 | } else if (token->type == AVIF_SAMPLE_TRANSFORM_NEGATION || token->type == AVIF_SAMPLE_TRANSFORM_ABSOLUTE || |
| 323 | token->type == AVIF_SAMPLE_TRANSFORM_NOT || token->type == AVIF_SAMPLE_TRANSFORM_BSR) { |
| 324 | AVIF_ASSERT_OR_RETURN(stackSize >= 1); |
| 325 | stack[stackSize - 1] = avifSampleTransformOperation32bOneOperand(stack[stackSize - 1], token->type); |
| 326 | // Pop one and push one. |
| 327 | } else { |
| 328 | AVIF_ASSERT_OR_RETURN(stackSize >= 2); |
| 329 | stack[stackSize - 2] = |
| 330 | avifSampleTransformOperation32bTwoOperands(stack[stackSize - 2], stack[stackSize - 1], token->type); |
| 331 | stackSize--; // Pop two and push one. |
| 332 | } |
| 333 | } |
| 334 | AVIF_ASSERT_OR_RETURN(stackSize == 1); |
| 335 | // Fit to the range defined by the PixelInformationProperty. |
| 336 | // The limited/full range is ignored, like in other libavif encoding and decoding paths. |
| 337 | stack[0] = AVIF_CLAMP(stack[0], minValue, maxValue); |
| 338 | |
| 339 | uint8_t * row = avifImagePlane(dstImage, c); |
| 340 | AVIF_ASSERT_OR_RETURN(row != NULL); |
| 341 | row += (size_t)avifImagePlaneRowBytes(dstImage, c) * y; |
no test coverage detected