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#include "SDL_internal.h"
22
23// This is the software implementation of the YUV texture support
24
25#ifdef SDL_HAVE_YUV
26
27#include "SDL_yuv_sw_c.h"
28#include "../video/SDL_surface_c.h"
29#include "../video/SDL_yuv_c.h"
30
31SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspace colorspace, int w, int h)
32{
33 SDL_SW_YUVTexture *swdata;
34
35 switch (format) {
36 case SDL_PIXELFORMAT_YV12:
37 case SDL_PIXELFORMAT_IYUV:
38 case SDL_PIXELFORMAT_YUY2:
39 case SDL_PIXELFORMAT_UYVY:
40 case SDL_PIXELFORMAT_YVYU:
41 case SDL_PIXELFORMAT_NV12:
42 case SDL_PIXELFORMAT_NV21:
43 break;
44 default:
45 SDL_SetError("Unsupported YUV format");
46 return NULL;
47 }
48
49 swdata = (SDL_SW_YUVTexture *)SDL_calloc(1, sizeof(*swdata));
50 if (!swdata) {
51 return NULL;
52 }
53
54 swdata->format = format;
55 swdata->colorspace = colorspace;
56 swdata->target_format = SDL_PIXELFORMAT_UNKNOWN;
57 swdata->w = w;
58 swdata->h = h;
59 {
60 size_t dst_size;
61 if (!SDL_CalculateYUVSize(format, w, h, &dst_size, NULL)) {
62 SDL_SW_DestroyYUVTexture(swdata);
63 return NULL;
64 }
65 swdata->pixels = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), dst_size);
66 if (!swdata->pixels) {
67 SDL_SW_DestroyYUVTexture(swdata);
68 return NULL;
69 }
70 }
71
72 // Find the pitch and offset values for the texture
73 switch (format) {
74 case SDL_PIXELFORMAT_YV12:
75 case SDL_PIXELFORMAT_IYUV:
76 swdata->pitches[0] = w;
77 swdata->pitches[1] = (swdata->pitches[0] + 1) / 2;
78 swdata->pitches[2] = (swdata->pitches[0] + 1) / 2;
79 swdata->planes[0] = swdata->pixels;
80 swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
81 swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
82 break;
83 case SDL_PIXELFORMAT_YUY2:
84 case SDL_PIXELFORMAT_UYVY:
85 case SDL_PIXELFORMAT_YVYU:
86 swdata->pitches[0] = ((w + 1) / 2) * 4;
87 swdata->planes[0] = swdata->pixels;
88 break;
89
90 case SDL_PIXELFORMAT_NV12:
91 case SDL_PIXELFORMAT_NV21:
92 swdata->pitches[0] = w;
93 swdata->pitches[1] = 2 * ((swdata->pitches[0] + 1) / 2);
94 swdata->planes[0] = swdata->pixels;
95 swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
96 break;
97
98 default:
99 SDL_assert(!"We should never get here (caught above)");
100 break;
101 }
102
103 // We're all done..
104 return swdata;
105}
106
107bool SDL_SW_QueryYUVTexturePixels(SDL_SW_YUVTexture *swdata, void **pixels,
108 int *pitch)
109{
110 *pixels = swdata->planes[0];
111 *pitch = swdata->pitches[0];
112 return true;
113}
114
115bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
116 const void *pixels, int pitch)
117{
118 switch (swdata->format) {
119 case SDL_PIXELFORMAT_YV12:
120 case SDL_PIXELFORMAT_IYUV:
121 if (rect->x == 0 && rect->y == 0 &&
122 rect->w == swdata->w && rect->h == swdata->h) {
123 SDL_memcpy(swdata->pixels, pixels,
124 (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
125 } else {
126 Uint8 *src, *dst;
127 int row;
128 size_t length;
129
130 // Copy the Y plane
131 src = (Uint8 *)pixels;
132 dst = swdata->pixels + rect->y * swdata->w + rect->x;
133 length = rect->w;
134 for (row = 0; row < rect->h; ++row) {
135 SDL_memcpy(dst, src, length);
136 src += pitch;
137 dst += swdata->w;
138 }
139
140 // Copy the next plane
141 src = (Uint8 *)pixels + rect->h * pitch;
142 dst = swdata->pixels + swdata->h * swdata->w;
143 dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
144 length = (rect->w + 1) / 2;
145 for (row = 0; row < (rect->h + 1) / 2; ++row) {
146 SDL_memcpy(dst, src, length);
147 src += (pitch + 1) / 2;
148 dst += (swdata->w + 1) / 2;
149 }
150
151 // Copy the next plane
152 src = (Uint8 *)pixels + rect->h * pitch + ((rect->h + 1) / 2) * ((pitch + 1) / 2);
153 dst = swdata->pixels + swdata->h * swdata->w +
154 ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
155 dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
156 length = (rect->w + 1) / 2;
157 for (row = 0; row < (rect->h + 1) / 2; ++row) {
158 SDL_memcpy(dst, src, length);
159 src += (pitch + 1) / 2;
160 dst += (swdata->w + 1) / 2;
161 }
162 }
163 break;
164 case SDL_PIXELFORMAT_YUY2:
165 case SDL_PIXELFORMAT_UYVY:
166 case SDL_PIXELFORMAT_YVYU:
167 {
168 Uint8 *src, *dst;
169 int row;
170 size_t length;
171
172 src = (Uint8 *)pixels;
173 dst =
174 swdata->planes[0] + rect->y * swdata->pitches[0] +
175 rect->x * 2;
176 length = 4 * (((size_t)rect->w + 1) / 2);
177 for (row = 0; row < rect->h; ++row) {
178 SDL_memcpy(dst, src, length);
179 src += pitch;
180 dst += swdata->pitches[0];
181 }
182 } break;
183 case SDL_PIXELFORMAT_NV12:
184 case SDL_PIXELFORMAT_NV21:
185 {
186 if (rect->x == 0 && rect->y == 0 && rect->w == swdata->w && rect->h == swdata->h) {
187 SDL_memcpy(swdata->pixels, pixels,
188 (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
189 } else {
190
191 Uint8 *src, *dst;
192 int row;
193 size_t length;
194
195 // Copy the Y plane
196 src = (Uint8 *)pixels;
197 dst = swdata->pixels + rect->y * swdata->w + rect->x;
198 length = rect->w;
199 for (row = 0; row < rect->h; ++row) {
200 SDL_memcpy(dst, src, length);
201 src += pitch;
202 dst += swdata->w;
203 }
204
205 // Copy the next plane
206 src = (Uint8 *)pixels + rect->h * pitch;
207 dst = swdata->pixels + swdata->h * swdata->w;
208 dst += 2 * ((rect->y + 1) / 2) * ((swdata->w + 1) / 2) + 2 * (rect->x / 2);
209 length = 2 * (((size_t)rect->w + 1) / 2);
210 for (row = 0; row < (rect->h + 1) / 2; ++row) {
211 SDL_memcpy(dst, src, length);
212 src += 2 * ((pitch + 1) / 2);
213 dst += 2 * ((swdata->w + 1) / 2);
214 }
215 }
216 } break;
217 default:
218 return SDL_SetError("Unsupported YUV format");
219 }
220 return true;
221}
222
223bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
224 const Uint8 *Yplane, int Ypitch,
225 const Uint8 *Uplane, int Upitch,
226 const Uint8 *Vplane, int Vpitch)
227{
228 const Uint8 *src;
229 Uint8 *dst;
230 int row;
231 size_t length;
232
233 // Copy the Y plane
234 src = Yplane;
235 dst = swdata->pixels + rect->y * swdata->w + rect->x;
236 length = rect->w;
237 for (row = 0; row < rect->h; ++row) {
238 SDL_memcpy(dst, src, length);
239 src += Ypitch;
240 dst += swdata->w;
241 }
242
243 // Copy the U plane
244 src = Uplane;
245 if (swdata->format == SDL_PIXELFORMAT_IYUV) {
246 dst = swdata->pixels + swdata->h * swdata->w;
247 } else {
248 dst = swdata->pixels + swdata->h * swdata->w +
249 ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
250 }
251 dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
252 length = (rect->w + 1) / 2;
253 for (row = 0; row < (rect->h + 1) / 2; ++row) {
254 SDL_memcpy(dst, src, length);
255 src += Upitch;
256 dst += (swdata->w + 1) / 2;
257 }
258
259 // Copy the V plane
260 src = Vplane;
261 if (swdata->format == SDL_PIXELFORMAT_YV12) {
262 dst = swdata->pixels + swdata->h * swdata->w;
263 } else {
264 dst = swdata->pixels + swdata->h * swdata->w +
265 ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
266 }
267 dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
268 length = (rect->w + 1) / 2;
269 for (row = 0; row < (rect->h + 1) / 2; ++row) {
270 SDL_memcpy(dst, src, length);
271 src += Vpitch;
272 dst += (swdata->w + 1) / 2;
273 }
274 return true;
275}
276
277bool SDL_SW_UpdateNVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
278 const Uint8 *Yplane, int Ypitch,
279 const Uint8 *UVplane, int UVpitch)
280{
281 const Uint8 *src;
282 Uint8 *dst;
283 int row;
284 size_t length;
285
286 // Copy the Y plane
287 src = Yplane;
288 dst = swdata->pixels + rect->y * swdata->w + rect->x;
289 length = rect->w;
290 for (row = 0; row < rect->h; ++row) {
291 SDL_memcpy(dst, src, length);
292 src += Ypitch;
293 dst += swdata->w;
294 }
295
296 // Copy the UV or VU plane
297 src = UVplane;
298 dst = swdata->pixels + swdata->h * swdata->w;
299 dst += rect->y * ((swdata->w + 1) / 2) + rect->x;
300 length = (rect->w + 1) / 2;
301 length *= 2;
302 for (row = 0; row < (rect->h + 1) / 2; ++row) {
303 SDL_memcpy(dst, src, length);
304 src += UVpitch;
305 dst += 2 * ((swdata->w + 1) / 2);
306 }
307
308 return true;
309}
310
311bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
312 void **pixels, int *pitch)
313{
314 switch (swdata->format) {
315 case SDL_PIXELFORMAT_YV12:
316 case SDL_PIXELFORMAT_IYUV:
317 case SDL_PIXELFORMAT_NV12:
318 case SDL_PIXELFORMAT_NV21:
319 if (rect && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w || rect->h != swdata->h)) {
320 return SDL_SetError("YV12, IYUV, NV12, NV21 textures only support full surface locks");
321 }
322 break;
323 default:
324 return SDL_SetError("Unsupported YUV format");
325 }
326
327 if (rect) {
328 *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * 2;
329 } else {
330 *pixels = swdata->planes[0];
331 }
332 *pitch = swdata->pitches[0];
333 return true;
334}
335
336void SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture *swdata)
337{
338}
339
340bool SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture *swdata, const SDL_Rect *srcrect, SDL_PixelFormat target_format, int w, int h, void *pixels, int pitch)
341{
342 int stretch;
343
344 // Make sure we're set up to display in the desired format
345 if (target_format != swdata->target_format && swdata->display) {
346 SDL_DestroySurface(swdata->display);
347 swdata->display = NULL;
348 }
349
350 stretch = 0;
351 if (srcrect->x || srcrect->y || srcrect->w < swdata->w || srcrect->h < swdata->h) {
352 /* The source rectangle has been clipped.
353 Using a scratch surface is easier than adding clipped
354 source support to all the blitters, plus that would
355 slow them down in the general unclipped case.
356 */
357 stretch = 1;
358 } else if ((srcrect->w != w) || (srcrect->h != h)) {
359 stretch = 1;
360 }
361 if (stretch) {
362 if (swdata->display) {
363 swdata->display->w = w;
364 swdata->display->h = h;
365 swdata->display->pixels = pixels;
366 swdata->display->pitch = pitch;
367 } else {
368 swdata->display = SDL_CreateSurfaceFrom(w, h, target_format, pixels, pitch);
369 if (!swdata->display) {
370 return false;
371 }
372 swdata->target_format = target_format;
373 }
374 if (!swdata->stretch) {
375 swdata->stretch = SDL_CreateSurface(swdata->w, swdata->h, target_format);
376 if (!swdata->stretch) {
377 return false;
378 }
379 }
380 pixels = swdata->stretch->pixels;
381 pitch = swdata->stretch->pitch;
382 }
383 if (!SDL_ConvertPixelsAndColorspace(swdata->w, swdata->h, swdata->format, swdata->colorspace, 0, swdata->planes[0], swdata->pitches[0], target_format, SDL_COLORSPACE_SRGB, 0, pixels, pitch)) {
384 return false;
385 }
386 if (stretch) {
387 SDL_Rect rect = *srcrect;
388 return SDL_StretchSurface(swdata->stretch, &rect, swdata->display, NULL, SDL_SCALEMODE_NEAREST);
389 } else {
390 return true;
391 }
392}
393
394void SDL_SW_DestroyYUVTexture(SDL_SW_YUVTexture *swdata)
395{
396 if (swdata) {
397 SDL_aligned_free(swdata->pixels);
398 SDL_DestroySurface(swdata->stretch);
399 SDL_DestroySurface(swdata->display);
400 SDL_free(swdata);
401 }
402}
403
404#endif // SDL_HAVE_YUV
405