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 | |
32 | SDL_SW_YUVTexture * |
33 | SDL_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 | |
131 | int |
132 | SDL_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 | |
140 | int |
141 | SDL_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 | |
248 | int |
249 | SDL_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 | |
303 | int 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 | |
337 | int |
338 | SDL_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 | |
364 | void |
365 | SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture * swdata) |
366 | { |
367 | } |
368 | |
369 | int |
370 | SDL_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 | |
439 | void |
440 | SDL_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 | |