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)10 avifBool 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