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