| // Copyright 2020 Joe Drago. All rights reserved. | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #include "compare.h" | |
| #include <math.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| avifBool compareYUVA(ImageComparison * ic, avifImage * image1, avifImage * image2) | |
| { | |
| memset(ic, 0, sizeof(ImageComparison)); | |
| if (!image1 || !image2) { | |
| return AVIF_FALSE; | |
| } | |
| if ((image1->width != image2->width) || (image1->height != image2->height) || (image1->depth != image2->depth) || | |
| (image1->yuvFormat != image2->yuvFormat) || (image1->yuvRange != image2->yuvRange)) { | |
| return AVIF_FALSE; | |
| } | |
| avifPixelFormatInfo formatInfo; | |
| avifGetPixelFormatInfo(image1->yuvFormat, &formatInfo); | |
| unsigned int uvW = image1->width >> formatInfo.chromaShiftX; | |
| if (uvW < 1) { | |
| uvW = 1; | |
| } | |
| unsigned int uvH = image1->height >> formatInfo.chromaShiftY; | |
| if (uvH < 1) { | |
| uvH = 1; | |
| } | |
| if (image1->depth == 8) { | |
| // uint8_t path | |
| uint8_t maxChannel = 255; | |
| for (unsigned int j = 0; j < image1->height; ++j) { | |
| for (unsigned int i = 0; i < image1->width; ++i) { | |
| uint8_t * Y1 = &image1->yuvPlanes[AVIF_CHAN_Y][i + (j * image1->yuvRowBytes[AVIF_CHAN_Y])]; | |
| uint8_t * Y2 = &image2->yuvPlanes[AVIF_CHAN_Y][i + (j * image2->yuvRowBytes[AVIF_CHAN_Y])]; | |
| int diffY = abs(*Y1 - *Y2); | |
| if (ic->maxDiffY < diffY) { | |
| ic->maxDiffY = diffY; | |
| } | |
| ic->avgDiffY += (float)diffY; | |
| uint8_t A1 = maxChannel; | |
| if (image1->alphaPlane) { | |
| A1 = image1->alphaPlane[i + (j * image1->alphaRowBytes)]; | |
| } | |
| uint8_t A2 = maxChannel; | |
| if (image2->alphaPlane) { | |
| A2 = image2->alphaPlane[i + (j * image1->alphaRowBytes)]; | |
| } | |
| int aDiff = abs(A1 - A2); | |
| if (ic->maxDiffA < aDiff) { | |
| ic->maxDiffA = aDiff; | |
| } | |
| ic->avgDiffA += (float)aDiff; | |
| } | |
| } | |
| for (unsigned int j = 0; j < uvH; ++j) { | |
| for (unsigned int i = 0; i < uvW; ++i) { | |
| uint8_t * U1 = &image1->yuvPlanes[AVIF_CHAN_U][i + (j * image1->yuvRowBytes[AVIF_CHAN_U])]; | |
| uint8_t * U2 = &image2->yuvPlanes[AVIF_CHAN_U][i + (j * image2->yuvRowBytes[AVIF_CHAN_U])]; | |
| int diffU = abs(*U1 - *U2); | |
| if (ic->maxDiffU < diffU) { | |
| ic->maxDiffU = diffU; | |
| } | |
| ic->avgDiffU += (float)diffU; | |
| uint8_t * V1 = &image1->yuvPlanes[AVIF_CHAN_V][i + (j * image1->yuvRowBytes[AVIF_CHAN_V])]; | |
| uint8_t * V2 = &image2->yuvPlanes[AVIF_CHAN_V][i + (j * image2->yuvRowBytes[AVIF_CHAN_V])]; | |
| int diffV = abs(*V1 - *V2); | |
| if (ic->maxDiffV < diffV) { | |
| ic->maxDiffV = diffV; | |
| } | |
| ic->avgDiffV += (float)diffV; | |
| } | |
| } | |
| } else { | |
| // uint16_t path | |
| uint16_t maxChannel = (uint16_t)((1 << image1->depth) - 1); | |
| for (unsigned int j = 0; j < image1->height; ++j) { | |
| for (unsigned int i = 0; i < image1->width; ++i) { | |
| uint16_t * Y1 = | |
| (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_Y][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_Y])]; | |
| uint16_t * Y2 = | |
| (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_Y][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_Y])]; | |
| int diffY = abs(*Y1 - *Y2); | |
| if (ic->maxDiffY < diffY) { | |
| ic->maxDiffY = diffY; | |
| } | |
| ic->avgDiffY += (float)diffY; | |
| uint16_t A1 = maxChannel; | |
| if (image1->alphaPlane) { | |
| A1 = *((uint16_t *)&image1->alphaPlane[(i * sizeof(uint16_t)) + (j * image1->alphaRowBytes)]); | |
| } | |
| uint16_t A2 = maxChannel; | |
| if (image2->alphaPlane) { | |
| A2 = *((uint16_t *)&image2->alphaPlane[(i * sizeof(uint16_t)) + (j * image2->alphaRowBytes)]); | |
| } | |
| int aDiff = abs(A1 - A2); | |
| if (ic->maxDiffA < aDiff) { | |
| ic->maxDiffA = aDiff; | |
| } | |
| ic->avgDiffA += (float)aDiff; | |
| } | |
| } | |
| for (unsigned int j = 0; j < uvH; ++j) { | |
| for (unsigned int i = 0; i < uvW; ++i) { | |
| uint16_t * U1 = | |
| (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_U][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_U])]; | |
| uint16_t * U2 = | |
| (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_U][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_U])]; | |
| int diffU = abs(*U1 - *U2); | |
| if (ic->maxDiffU < diffU) { | |
| ic->maxDiffU = diffU; | |
| } | |
| ic->avgDiffU += (float)diffU; | |
| uint16_t * V1 = | |
| (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_V][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_V])]; | |
| uint16_t * V2 = | |
| (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_V][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_V])]; | |
| int diffV = abs(*V1 - *V2); | |
| if (ic->maxDiffV < diffV) { | |
| ic->maxDiffV = diffV; | |
| } | |
| ic->avgDiffV += (float)diffV; | |
| } | |
| } | |
| } | |
| float totalPixels = (float)(image1->width * image1->height); | |
| ic->avgDiffY /= totalPixels; | |
| ic->avgDiffU /= totalPixels; | |
| ic->avgDiffV /= totalPixels; | |
| ic->avgDiffA /= totalPixels; | |
| ic->maxDiff = ic->maxDiffY; | |
| if (ic->maxDiff < ic->maxDiffU) { | |
| ic->maxDiff = ic->maxDiffU; | |
| } | |
| if (ic->maxDiff < ic->maxDiffV) { | |
| ic->maxDiff = ic->maxDiffV; | |
| } | |
| if (ic->maxDiff < ic->maxDiffA) { | |
| ic->maxDiff = ic->maxDiffA; | |
| } | |
| ic->avgDiff = (ic->avgDiffY + ic->avgDiffU + ic->avgDiffV + ic->avgDiffA) / 4.0f; | |
| return AVIF_TRUE; | |
| } |