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