1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | /* |
23 | |
24 | Based on automated SDL_Surface tests originally written by Edgar Simo 'bobbens'. |
25 | |
26 | Rewritten for test lib by Andreas Schiffler. |
27 | |
28 | */ |
29 | #include <SDL3/SDL_test.h> |
30 | |
31 | #define FILENAME_SIZE 128 |
32 | |
33 | /* Counter for _CompareSurface calls; used for filename creation when comparisons fail */ |
34 | static int _CompareSurfaceCount = 0; |
35 | |
36 | /* Compare surfaces */ |
37 | int SDLTest_CompareSurfaces(SDL_Surface *surface, SDL_Surface *referenceSurface, int allowable_error) |
38 | { |
39 | int ret; |
40 | int i, j; |
41 | int dist; |
42 | int sampleErrorX = 0, sampleErrorY = 0, sampleDist = 0; |
43 | SDL_Color sampleReference = { 0, 0, 0, 0 }; |
44 | SDL_Color sampleActual = { 0, 0, 0, 0 }; |
45 | Uint8 R, G, B, A; |
46 | Uint8 Rd, Gd, Bd, Ad; |
47 | char imageFilename[FILENAME_SIZE]; |
48 | char referenceFilename[FILENAME_SIZE]; |
49 | |
50 | /* Validate input surfaces */ |
51 | if (!surface) { |
52 | SDLTest_LogError("Cannot compare NULL surface" ); |
53 | return -1; |
54 | } |
55 | |
56 | if (!referenceSurface) { |
57 | SDLTest_LogError("Cannot compare NULL reference surface" ); |
58 | return -1; |
59 | } |
60 | |
61 | /* Make sure surface size is the same. */ |
62 | if ((surface->w != referenceSurface->w) || (surface->h != referenceSurface->h)) { |
63 | SDLTest_LogError("Expected %dx%d surface, got %dx%d" , referenceSurface->w, referenceSurface->h, surface->w, surface->h); |
64 | return -2; |
65 | } |
66 | |
67 | /* Sanitize input value */ |
68 | if (allowable_error < 0) { |
69 | allowable_error = 0; |
70 | } |
71 | |
72 | SDL_LockSurface(surface); |
73 | SDL_LockSurface(referenceSurface); |
74 | |
75 | ret = 0; |
76 | /* Compare image - should be same format. */ |
77 | for (j = 0; j < surface->h; j++) { |
78 | for (i = 0; i < surface->w; i++) { |
79 | int temp; |
80 | |
81 | temp = SDL_ReadSurfacePixel(surface, i, j, &R, &G, &B, &A); |
82 | if (!temp) { |
83 | SDLTest_LogError("Failed to retrieve pixel (%d,%d): %s" , i, j, SDL_GetError()); |
84 | ret++; |
85 | continue; |
86 | } |
87 | |
88 | temp = SDL_ReadSurfacePixel(referenceSurface, i, j, &Rd, &Gd, &Bd, &Ad); |
89 | if (!temp) { |
90 | SDLTest_LogError("Failed to retrieve reference pixel (%d,%d): %s" , i, j, SDL_GetError()); |
91 | ret++; |
92 | continue; |
93 | } |
94 | |
95 | dist = 0; |
96 | dist += (R - Rd) * (R - Rd); |
97 | dist += (G - Gd) * (G - Gd); |
98 | dist += (B - Bd) * (B - Bd); |
99 | |
100 | /* Allow some difference in blending accuracy */ |
101 | if (dist > allowable_error) { |
102 | ret++; |
103 | if (ret == 1) { |
104 | sampleErrorX = i; |
105 | sampleErrorY = j; |
106 | sampleDist = dist; |
107 | sampleReference.r = Rd; |
108 | sampleReference.g = Gd; |
109 | sampleReference.b = Bd; |
110 | sampleReference.a = Ad; |
111 | sampleActual.r = R; |
112 | sampleActual.g = G; |
113 | sampleActual.b = B; |
114 | sampleActual.a = A; |
115 | } |
116 | } |
117 | } |
118 | } |
119 | |
120 | SDL_UnlockSurface(surface); |
121 | SDL_UnlockSurface(referenceSurface); |
122 | |
123 | /* Save test image and reference for analysis on failures */ |
124 | _CompareSurfaceCount++; |
125 | if (ret != 0) { |
126 | SDLTest_LogError("Comparison of pixels with allowable error of %i failed %i times." , allowable_error, ret); |
127 | SDLTest_LogError("Reference surface format: %s" , SDL_GetPixelFormatName(referenceSurface->format)); |
128 | SDLTest_LogError("Actual surface format: %s" , SDL_GetPixelFormatName(surface->format)); |
129 | SDLTest_LogError("First detected occurrence at position %i,%i with a squared RGB-difference of %i." , sampleErrorX, sampleErrorY, sampleDist); |
130 | SDLTest_LogError("Reference pixel: R=%u G=%u B=%u A=%u" , sampleReference.r, sampleReference.g, sampleReference.b, sampleReference.a); |
131 | SDLTest_LogError("Actual pixel : R=%u G=%u B=%u A=%u" , sampleActual.r, sampleActual.g, sampleActual.b, sampleActual.a); |
132 | (void)SDL_snprintf(imageFilename, FILENAME_SIZE - 1, "CompareSurfaces%04d_TestOutput.bmp" , _CompareSurfaceCount); |
133 | SDL_SaveBMP(surface, imageFilename); |
134 | (void)SDL_snprintf(referenceFilename, FILENAME_SIZE - 1, "CompareSurfaces%04d_Reference.bmp" , _CompareSurfaceCount); |
135 | SDL_SaveBMP(referenceSurface, referenceFilename); |
136 | SDLTest_LogError("Surfaces from failed comparison saved as '%s' and '%s'" , imageFilename, referenceFilename); |
137 | } |
138 | |
139 | return ret; |
140 | } |
141 | |
142 | int SDLTest_CompareMemory(const void *actual, size_t size_actual, const void *reference, size_t size_reference) { |
143 | #define WIDTH 16 |
144 | |
145 | const size_t size_max = SDL_max(size_actual, size_reference); |
146 | size_t i; |
147 | struct { |
148 | const char *; |
149 | const Uint8 *data; |
150 | size_t size; |
151 | } columns[] = { |
152 | { |
153 | "actual" , |
154 | actual, |
155 | size_actual, |
156 | }, |
157 | { |
158 | "reference" , |
159 | reference, |
160 | size_reference, |
161 | }, |
162 | }; |
163 | char line_buffer[16 + SDL_arraysize(columns) * (4 * WIDTH + 1) + (SDL_arraysize(columns) - 1) * 2 + 1]; |
164 | |
165 | SDLTest_AssertCheck(size_actual == size_reference, "Sizes of memory blocks must be equal (actual=%" SDL_PRIu64 " expected=%" SDL_PRIu64 ")" , (Uint64)size_actual, (Uint64)size_reference); |
166 | if (size_actual == size_reference) { |
167 | int equals; |
168 | equals = SDL_memcmp(actual, reference, size_max) == 0; |
169 | SDLTest_AssertCheck(equals, "Memory blocks contain the same data" ); |
170 | if (equals) { |
171 | return 0; |
172 | } |
173 | } |
174 | |
175 | SDL_memset(line_buffer, ' ', sizeof(line_buffer)); |
176 | line_buffer[sizeof(line_buffer) - 1] = '\0'; |
177 | for (i = 0; i < SDL_arraysize(columns); i++) { |
178 | SDL_memcpy(line_buffer + 16 + 1 + i * (4 * WIDTH + 3), columns[i].header, SDL_strlen(columns[i].header)); |
179 | } |
180 | SDLTest_LogError("%s" , line_buffer); |
181 | |
182 | for (i = 0; i < size_max; i += WIDTH) { |
183 | size_t pos = 0; |
184 | size_t col; |
185 | |
186 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%016" SDL_PRIx64 , (Uint64)i); |
187 | |
188 | for (col = 0; col < SDL_arraysize(columns); col++) { |
189 | size_t j; |
190 | |
191 | for (j = 0; j < WIDTH; j++) { |
192 | if (i + j < columns[col].size) { |
193 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " %02x" , columns[col].data[i + j]); |
194 | } else { |
195 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " " ); |
196 | } |
197 | } |
198 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " " ); |
199 | for (j = 0; j < WIDTH; j++) { |
200 | char c = ' '; |
201 | if (i + j < columns[col].size) { |
202 | c = columns[col].data[i + j]; |
203 | if (!SDL_isprint(c)) { |
204 | c = '.'; |
205 | } |
206 | } |
207 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%c" , c); |
208 | } |
209 | if (col < SDL_arraysize(columns) - 1) { |
210 | pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer), " |" ); |
211 | } |
212 | } |
213 | SDLTest_LogError("%s" , line_buffer); |
214 | SDL_assert(pos == SDL_arraysize(line_buffer) - 1); |
215 | } |
216 | #undef WIDTH |
217 | return 1; |
218 | } |
219 | |