1 // Copyright 2020 Joe Drago. All rights reserved. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 #include "compare.h" 5 6 #include <math.h> 7 #include <stdlib.h> 8 #include <string.h> 9 compareYUVA(ImageComparison * ic,const avifImage * image1,const avifImage * image2)10avifBool compareYUVA(ImageComparison * ic, const avifImage * image1, const avifImage * image2) 11 { 12 memset(ic, 0, sizeof(ImageComparison)); 13 if (!image1 || !image2) { 14 return AVIF_FALSE; 15 } 16 if ((image1->width != image2->width) || (image1->height != image2->height) || (image1->depth != image2->depth) || 17 (image1->yuvFormat != image2->yuvFormat) || (image1->yuvRange != image2->yuvRange)) { 18 return AVIF_FALSE; 19 } 20 21 avifPixelFormatInfo formatInfo; 22 avifGetPixelFormatInfo(image1->yuvFormat, &formatInfo); 23 unsigned int uvW = image1->width >> formatInfo.chromaShiftX; 24 if (uvW < 1) { 25 uvW = 1; 26 } 27 unsigned int uvH = image1->height >> formatInfo.chromaShiftY; 28 if (uvH < 1) { 29 uvH = 1; 30 } 31 32 if (image1->depth == 8) { 33 // uint8_t path 34 uint8_t maxChannel = 255; 35 36 for (unsigned int j = 0; j < image1->height; ++j) { 37 for (unsigned int i = 0; i < image1->width; ++i) { 38 uint8_t * Y1 = &image1->yuvPlanes[AVIF_CHAN_Y][i + (j * image1->yuvRowBytes[AVIF_CHAN_Y])]; 39 uint8_t * Y2 = &image2->yuvPlanes[AVIF_CHAN_Y][i + (j * image2->yuvRowBytes[AVIF_CHAN_Y])]; 40 41 int diffY = abs(*Y1 - *Y2); 42 if (ic->maxDiffY < diffY) { 43 ic->maxDiffY = diffY; 44 } 45 ic->avgDiffY += (float)diffY; 46 47 uint8_t A1 = maxChannel; 48 if (image1->alphaPlane) { 49 A1 = image1->alphaPlane[i + (j * image1->alphaRowBytes)]; 50 } 51 uint8_t A2 = maxChannel; 52 if (image2->alphaPlane) { 53 A2 = image2->alphaPlane[i + (j * image1->alphaRowBytes)]; 54 } 55 56 int aDiff = abs(A1 - A2); 57 if (ic->maxDiffA < aDiff) { 58 ic->maxDiffA = aDiff; 59 } 60 ic->avgDiffA += (float)aDiff; 61 } 62 } 63 64 for (unsigned int j = 0; j < uvH; ++j) { 65 for (unsigned int i = 0; i < uvW; ++i) { 66 uint8_t * U1 = &image1->yuvPlanes[AVIF_CHAN_U][i + (j * image1->yuvRowBytes[AVIF_CHAN_U])]; 67 uint8_t * U2 = &image2->yuvPlanes[AVIF_CHAN_U][i + (j * image2->yuvRowBytes[AVIF_CHAN_U])]; 68 int diffU = abs(*U1 - *U2); 69 if (ic->maxDiffU < diffU) { 70 ic->maxDiffU = diffU; 71 } 72 ic->avgDiffU += (float)diffU; 73 74 uint8_t * V1 = &image1->yuvPlanes[AVIF_CHAN_V][i + (j * image1->yuvRowBytes[AVIF_CHAN_V])]; 75 uint8_t * V2 = &image2->yuvPlanes[AVIF_CHAN_V][i + (j * image2->yuvRowBytes[AVIF_CHAN_V])]; 76 int diffV = abs(*V1 - *V2); 77 if (ic->maxDiffV < diffV) { 78 ic->maxDiffV = diffV; 79 } 80 ic->avgDiffV += (float)diffV; 81 } 82 } 83 } else { 84 // uint16_t path 85 86 uint16_t maxChannel = (uint16_t)((1 << image1->depth) - 1); 87 for (unsigned int j = 0; j < image1->height; ++j) { 88 for (unsigned int i = 0; i < image1->width; ++i) { 89 uint16_t * Y1 = 90 (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_Y][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_Y])]; 91 uint16_t * Y2 = 92 (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_Y][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_Y])]; 93 94 int diffY = abs(*Y1 - *Y2); 95 if (ic->maxDiffY < diffY) { 96 ic->maxDiffY = diffY; 97 } 98 ic->avgDiffY += (float)diffY; 99 100 uint16_t A1 = maxChannel; 101 if (image1->alphaPlane) { 102 A1 = *((uint16_t *)&image1->alphaPlane[(i * sizeof(uint16_t)) + (j * image1->alphaRowBytes)]); 103 } 104 uint16_t A2 = maxChannel; 105 if (image2->alphaPlane) { 106 A2 = *((uint16_t *)&image2->alphaPlane[(i * sizeof(uint16_t)) + (j * image2->alphaRowBytes)]); 107 } 108 109 int aDiff = abs(A1 - A2); 110 if (ic->maxDiffA < aDiff) { 111 ic->maxDiffA = aDiff; 112 } 113 ic->avgDiffA += (float)aDiff; 114 } 115 } 116 117 for (unsigned int j = 0; j < uvH; ++j) { 118 for (unsigned int i = 0; i < uvW; ++i) { 119 uint16_t * U1 = 120 (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_U][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_U])]; 121 uint16_t * U2 = 122 (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_U][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_U])]; 123 int diffU = abs(*U1 - *U2); 124 if (ic->maxDiffU < diffU) { 125 ic->maxDiffU = diffU; 126 } 127 ic->avgDiffU += (float)diffU; 128 129 uint16_t * V1 = 130 (uint16_t *)&image1->yuvPlanes[AVIF_CHAN_V][(i * sizeof(uint16_t)) + (j * image1->yuvRowBytes[AVIF_CHAN_V])]; 131 uint16_t * V2 = 132 (uint16_t *)&image2->yuvPlanes[AVIF_CHAN_V][(i * sizeof(uint16_t)) + (j * image2->yuvRowBytes[AVIF_CHAN_V])]; 133 int diffV = abs(*V1 - *V2); 134 if (ic->maxDiffV < diffV) { 135 ic->maxDiffV = diffV; 136 } 137 ic->avgDiffV += (float)diffV; 138 } 139 } 140 } 141 142 float totalPixels = (float)(image1->width * image1->height); 143 ic->avgDiffY /= totalPixels; 144 ic->avgDiffU /= totalPixels; 145 ic->avgDiffV /= totalPixels; 146 ic->avgDiffA /= totalPixels; 147 148 ic->maxDiff = ic->maxDiffY; 149 if (ic->maxDiff < ic->maxDiffU) { 150 ic->maxDiff = ic->maxDiffU; 151 } 152 if (ic->maxDiff < ic->maxDiffV) { 153 ic->maxDiff = ic->maxDiffV; 154 } 155 if (ic->maxDiff < ic->maxDiffA) { 156 ic->maxDiff = ic->maxDiffA; 157 } 158 159 ic->avgDiff = (ic->avgDiffY + ic->avgDiffU + ic->avgDiffV + ic->avgDiffA) / 4.0f; 160 return AVIF_TRUE; 161 } 162