| // Copyright 2021 Joe Drago. All rights reserved. | 
 | // SPDX-License-Identifier: BSD-2-Clause | 
 |  | 
 | #include "avif/internal.h" | 
 |  | 
 | #if !defined(AVIF_LIBYUV_ENABLED) | 
 |  | 
 | avifBool avifImageScale(avifImage * image, | 
 |                         uint32_t dstWidth, | 
 |                         uint32_t dstHeight, | 
 |                         uint32_t imageSizeLimit, | 
 |                         uint32_t imageDimensionLimit, | 
 |                         avifDiagnostics * diag) | 
 | { | 
 |     (void)image; | 
 |     (void)dstWidth; | 
 |     (void)dstHeight; | 
 |     (void)imageSizeLimit; | 
 |     (void)imageDimensionLimit; | 
 |     avifDiagnosticsPrintf(diag, "avifImageScale() called, but is unimplemented without libyuv!"); | 
 |     return AVIF_FALSE; | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | #include <limits.h> | 
 |  | 
 | #if defined(__clang__) | 
 | #pragma clang diagnostic push | 
 | #pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype" | 
 | // The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored | 
 | // in version 1813: | 
 | // https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182 | 
 | // https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834 | 
 | #pragma clang diagnostic ignored "-Wnewline-eof"       // "no newline at end of file" | 
 | #endif | 
 | #include <libyuv.h> | 
 | #if defined(__clang__) | 
 | #pragma clang diagnostic pop | 
 | #endif | 
 |  | 
 | // This should be configurable and/or smarter. kFilterBox has the highest quality but is the slowest. | 
 | #define AVIF_LIBYUV_FILTER_MODE kFilterBox | 
 |  | 
 | avifBool avifImageScale(avifImage * image, | 
 |                         uint32_t dstWidth, | 
 |                         uint32_t dstHeight, | 
 |                         uint32_t imageSizeLimit, | 
 |                         uint32_t imageDimensionLimit, | 
 |                         avifDiagnostics * diag) | 
 | { | 
 |     if ((image->width == dstWidth) && (image->height == dstHeight)) { | 
 |         // Nothing to do | 
 |         return AVIF_TRUE; | 
 |     } | 
 |  | 
 |     if ((dstWidth == 0) || (dstHeight == 0)) { | 
 |         avifDiagnosticsPrintf(diag, "avifImageScale requested invalid dst dimensions [%ux%u]", dstWidth, dstHeight); | 
 |         return AVIF_FALSE; | 
 |     } | 
 |     if (avifDimensionsTooLarge(dstWidth, dstHeight, imageSizeLimit, imageDimensionLimit)) { | 
 |         avifDiagnosticsPrintf(diag, "avifImageScale requested dst dimensions that are too large [%ux%u]", dstWidth, dstHeight); | 
 |         return AVIF_FALSE; | 
 |     } | 
 |  | 
 |     uint8_t * srcYUVPlanes[AVIF_PLANE_COUNT_YUV]; | 
 |     uint32_t srcYUVRowBytes[AVIF_PLANE_COUNT_YUV]; | 
 |     for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) { | 
 |         srcYUVPlanes[i] = image->yuvPlanes[i]; | 
 |         image->yuvPlanes[i] = NULL; | 
 |         srcYUVRowBytes[i] = image->yuvRowBytes[i]; | 
 |         image->yuvRowBytes[i] = 0; | 
 |     } | 
 |     const avifBool srcImageOwnsYUVPlanes = image->imageOwnsYUVPlanes; | 
 |     image->imageOwnsYUVPlanes = AVIF_FALSE; | 
 |  | 
 |     uint8_t * srcAlphaPlane = image->alphaPlane; | 
 |     image->alphaPlane = NULL; | 
 |     uint32_t srcAlphaRowBytes = image->alphaRowBytes; | 
 |     image->alphaRowBytes = 0; | 
 |     const avifBool srcImageOwnsAlphaPlane = image->imageOwnsAlphaPlane; | 
 |     image->imageOwnsAlphaPlane = AVIF_FALSE; | 
 |  | 
 |     const uint32_t srcWidth = image->width; | 
 |     const uint32_t srcHeight = image->height; | 
 |     const uint32_t srcUVWidth = avifImagePlaneWidth(image, AVIF_CHAN_U); | 
 |     const uint32_t srcUVHeight = avifImagePlaneHeight(image, AVIF_CHAN_U); | 
 |     image->width = dstWidth; | 
 |     image->height = dstHeight; | 
 |  | 
 |     if (srcYUVPlanes[0] || srcAlphaPlane) { | 
 |         // A simple conservative check to avoid integer overflows in libyuv's ScalePlane() and | 
 |         // ScalePlane_12() functions. | 
 |         if (srcWidth > 16384) { | 
 |             avifDiagnosticsPrintf(diag, "avifImageScale requested invalid width scale for libyuv [%u -> %u]", srcWidth, dstWidth); | 
 |             return AVIF_FALSE; | 
 |         } | 
 |         if (srcHeight > 16384) { | 
 |             avifDiagnosticsPrintf(diag, "avifImageScale requested invalid height scale for libyuv [%u -> %u]", srcHeight, dstHeight); | 
 |             return AVIF_FALSE; | 
 |         } | 
 |     } | 
 |  | 
 |     if (srcYUVPlanes[0]) { | 
 |         const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_YUV); | 
 |         if (allocationResult != AVIF_RESULT_OK) { | 
 |             avifDiagnosticsPrintf(diag, "Allocation of YUV planes failed: %s", avifResultToString(allocationResult)); | 
 |             return AVIF_FALSE; | 
 |         } | 
 |  | 
 |         for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) { | 
 |             if (!srcYUVPlanes[i]) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             const uint32_t srcW = (i == AVIF_CHAN_Y) ? srcWidth : srcUVWidth; | 
 |             const uint32_t srcH = (i == AVIF_CHAN_Y) ? srcHeight : srcUVHeight; | 
 |             const uint32_t dstW = avifImagePlaneWidth(image, i); | 
 |             const uint32_t dstH = avifImagePlaneHeight(image, i); | 
 |             if (image->depth > 8) { | 
 |                 uint16_t * const srcPlane = (uint16_t *)srcYUVPlanes[i]; | 
 |                 const uint32_t srcStride = srcYUVRowBytes[i] / 2; | 
 |                 uint16_t * const dstPlane = (uint16_t *)image->yuvPlanes[i]; | 
 |                 const uint32_t dstStride = image->yuvRowBytes[i] / 2; | 
 | #if LIBYUV_VERSION >= 1774 | 
 |                 ScalePlane_12(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); | 
 | #else | 
 |                 ScalePlane_16(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); | 
 | #endif | 
 |             } else { | 
 |                 uint8_t * const srcPlane = srcYUVPlanes[i]; | 
 |                 const uint32_t srcStride = srcYUVRowBytes[i]; | 
 |                 uint8_t * const dstPlane = image->yuvPlanes[i]; | 
 |                 const uint32_t dstStride = image->yuvRowBytes[i]; | 
 |                 ScalePlane(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); | 
 |             } | 
 |  | 
 |             if (srcImageOwnsYUVPlanes) { | 
 |                 avifFree(srcYUVPlanes[i]); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     if (srcAlphaPlane) { | 
 |         const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A); | 
 |         if (allocationResult != AVIF_RESULT_OK) { | 
 |             avifDiagnosticsPrintf(diag, "Allocation of alpha plane failed: %s", avifResultToString(allocationResult)); | 
 |             return AVIF_FALSE; | 
 |         } | 
 |  | 
 |         if (image->depth > 8) { | 
 |             uint16_t * const srcPlane = (uint16_t *)srcAlphaPlane; | 
 |             const uint32_t srcStride = srcAlphaRowBytes / 2; | 
 |             uint16_t * const dstPlane = (uint16_t *)image->alphaPlane; | 
 |             const uint32_t dstStride = image->alphaRowBytes / 2; | 
 | #if LIBYUV_VERSION >= 1774 | 
 |             ScalePlane_12(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); | 
 | #else | 
 |             ScalePlane_16(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); | 
 | #endif | 
 |         } else { | 
 |             uint8_t * const srcPlane = srcAlphaPlane; | 
 |             const uint32_t srcStride = srcAlphaRowBytes; | 
 |             uint8_t * const dstPlane = image->alphaPlane; | 
 |             const uint32_t dstStride = image->alphaRowBytes; | 
 |             ScalePlane(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); | 
 |         } | 
 |  | 
 |         if (srcImageOwnsAlphaPlane) { | 
 |             avifFree(srcAlphaPlane); | 
 |         } | 
 |     } | 
 |  | 
 |     return AVIF_TRUE; | 
 | } | 
 |  | 
 | #endif |