1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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 #include "SDL_assert.h"
28
29 #include "SDL_yuv_sw_c.h"
30
31
32 SDL_SW_YUVTexture *
SDL_SW_CreateYUVTexture(Uint32 format,int w,int h)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_malloc(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
SDL_SW_QueryYUVTexturePixels(SDL_SW_YUVTexture * swdata,void ** pixels,int * pitch)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
SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata,const SDL_Rect * rect,const void * pixels,int pitch)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
SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata,const SDL_Rect * rect,const Uint8 * Yplane,int Ypitch,const Uint8 * Uplane,int Upitch,const Uint8 * Vplane,int Vpitch)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_LockYUVTexture(SDL_SW_YUVTexture * swdata,const SDL_Rect * rect,void ** pixels,int * pitch)304 SDL_SW_LockYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
305 void **pixels, int *pitch)
306 {
307 switch (swdata->format) {
308 case SDL_PIXELFORMAT_YV12:
309 case SDL_PIXELFORMAT_IYUV:
310 case SDL_PIXELFORMAT_NV12:
311 case SDL_PIXELFORMAT_NV21:
312 if (rect
313 && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w
314 || rect->h != swdata->h)) {
315 return SDL_SetError
316 ("YV12, IYUV, NV12, NV21 textures only support full surface locks");
317 }
318 break;
319 }
320
321 if (rect) {
322 *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * 2;
323 } else {
324 *pixels = swdata->planes[0];
325 }
326 *pitch = swdata->pitches[0];
327 return 0;
328 }
329
330 void
SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture * swdata)331 SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture * swdata)
332 {
333 }
334
335 int
SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata,const SDL_Rect * srcrect,Uint32 target_format,int w,int h,void * pixels,int pitch)336 SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect,
337 Uint32 target_format, int w, int h, void *pixels,
338 int pitch)
339 {
340 int stretch;
341
342 /* Make sure we're set up to display in the desired format */
343 if (target_format != swdata->target_format && swdata->display) {
344 SDL_FreeSurface(swdata->display);
345 swdata->display = NULL;
346 }
347
348 stretch = 0;
349 if (srcrect->x || srcrect->y || srcrect->w < swdata->w || srcrect->h < swdata->h) {
350 /* The source rectangle has been clipped.
351 Using a scratch surface is easier than adding clipped
352 source support to all the blitters, plus that would
353 slow them down in the general unclipped case.
354 */
355 stretch = 1;
356 } else if ((srcrect->w != w) || (srcrect->h != h)) {
357 stretch = 1;
358 }
359 if (stretch) {
360 int bpp;
361 Uint32 Rmask, Gmask, Bmask, Amask;
362
363 if (swdata->display) {
364 swdata->display->w = w;
365 swdata->display->h = h;
366 swdata->display->pixels = pixels;
367 swdata->display->pitch = pitch;
368 } else {
369 /* This must have succeeded in SDL_SW_SetupYUVDisplay() earlier */
370 SDL_PixelFormatEnumToMasks(target_format, &bpp, &Rmask, &Gmask,
371 &Bmask, &Amask);
372 swdata->display =
373 SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask,
374 Gmask, Bmask, Amask);
375 if (!swdata->display) {
376 return (-1);
377 }
378 }
379 if (!swdata->stretch) {
380 /* This must have succeeded in SDL_SW_SetupYUVDisplay() earlier */
381 SDL_PixelFormatEnumToMasks(target_format, &bpp, &Rmask, &Gmask,
382 &Bmask, &Amask);
383 swdata->stretch =
384 SDL_CreateRGBSurface(0, swdata->w, swdata->h, bpp, Rmask,
385 Gmask, Bmask, Amask);
386 if (!swdata->stretch) {
387 return (-1);
388 }
389 }
390 pixels = swdata->stretch->pixels;
391 pitch = swdata->stretch->pitch;
392 }
393 if (SDL_ConvertPixels(swdata->w, swdata->h, swdata->format,
394 swdata->planes[0], swdata->pitches[0],
395 target_format, pixels, pitch) < 0) {
396 return -1;
397 }
398 if (stretch) {
399 SDL_Rect rect = *srcrect;
400 SDL_SoftStretch(swdata->stretch, &rect, swdata->display, NULL);
401 }
402 return 0;
403 }
404
405 void
SDL_SW_DestroyYUVTexture(SDL_SW_YUVTexture * swdata)406 SDL_SW_DestroyYUVTexture(SDL_SW_YUVTexture * swdata)
407 {
408 if (swdata) {
409 SDL_free(swdata->pixels);
410 SDL_FreeSurface(swdata->stretch);
411 SDL_FreeSurface(swdata->display);
412 SDL_free(swdata);
413 }
414 }
415
416 #endif /* SDL_HAVE_YUV */
417
418 /* vi: set ts=4 sw=4 expandtab: */
419