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 #if SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED
24 
25 #include "../SDL_sysrender.h"
26 #include "SDL_render_sw_c.h"
27 #include "SDL_hints.h"
28 #include "SDL_assert.h"
29 
30 #include "SDL_draw.h"
31 #include "SDL_blendfillrect.h"
32 #include "SDL_blendline.h"
33 #include "SDL_blendpoint.h"
34 #include "SDL_drawline.h"
35 #include "SDL_drawpoint.h"
36 #include "SDL_rotate.h"
37 
38 /* SDL surface based renderer implementation */
39 
40 typedef struct
41 {
42     const SDL_Rect *viewport;
43     const SDL_Rect *cliprect;
44     SDL_bool surface_cliprect_dirty;
45 } SW_DrawStateCache;
46 
47 typedef struct
48 {
49     SDL_Surface *surface;
50     SDL_Surface *window;
51 } SW_RenderData;
52 
53 
54 static SDL_Surface *
SW_ActivateRenderer(SDL_Renderer * renderer)55 SW_ActivateRenderer(SDL_Renderer * renderer)
56 {
57     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
58 
59     if (!data->surface) {
60         data->surface = data->window;
61     }
62     if (!data->surface) {
63         SDL_Surface *surface = SDL_GetWindowSurface(renderer->window);
64         if (surface) {
65             data->surface = data->window = surface;
66         }
67     }
68     return data->surface;
69 }
70 
71 static void
SW_WindowEvent(SDL_Renderer * renderer,const SDL_WindowEvent * event)72 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
73 {
74     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
75 
76     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
77         data->surface = NULL;
78         data->window = NULL;
79     }
80 }
81 
82 static int
SW_GetOutputSize(SDL_Renderer * renderer,int * w,int * h)83 SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
84 {
85     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
86 
87     if (data->surface) {
88         if (w) {
89             *w = data->surface->w;
90         }
91         if (h) {
92             *h = data->surface->h;
93         }
94         return 0;
95     }
96 
97     if (renderer->window) {
98         SDL_GetWindowSize(renderer->window, w, h);
99         return 0;
100     }
101 
102     SDL_SetError("Software renderer doesn't have an output surface");
103     return -1;
104 }
105 
106 static int
SW_CreateTexture(SDL_Renderer * renderer,SDL_Texture * texture)107 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
108 {
109     int bpp;
110     Uint32 Rmask, Gmask, Bmask, Amask;
111 
112     if (!SDL_PixelFormatEnumToMasks
113         (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
114         return SDL_SetError("Unknown texture format");
115     }
116 
117     texture->driverdata =
118         SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
119                              Bmask, Amask);
120     SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
121                            texture->b);
122     SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
123     SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
124 
125     /* Only RLE encode textures without an alpha channel since the RLE coder
126      * discards the color values of pixels with an alpha value of zero.
127      */
128     if (texture->access == SDL_TEXTUREACCESS_STATIC && !Amask) {
129         SDL_SetSurfaceRLE(texture->driverdata, 1);
130     }
131 
132     if (!texture->driverdata) {
133         return -1;
134     }
135     return 0;
136 }
137 
138 static int
SW_UpdateTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const void * pixels,int pitch)139 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
140                  const SDL_Rect * rect, const void *pixels, int pitch)
141 {
142     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
143     Uint8 *src, *dst;
144     int row;
145     size_t length;
146 
147     if(SDL_MUSTLOCK(surface))
148         SDL_LockSurface(surface);
149     src = (Uint8 *) pixels;
150     dst = (Uint8 *) surface->pixels +
151                         rect->y * surface->pitch +
152                         rect->x * surface->format->BytesPerPixel;
153     length = rect->w * surface->format->BytesPerPixel;
154     for (row = 0; row < rect->h; ++row) {
155         SDL_memcpy(dst, src, length);
156         src += pitch;
157         dst += surface->pitch;
158     }
159     if(SDL_MUSTLOCK(surface))
160         SDL_UnlockSurface(surface);
161     return 0;
162 }
163 
164 static int
SW_LockTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,void ** pixels,int * pitch)165 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
166                const SDL_Rect * rect, void **pixels, int *pitch)
167 {
168     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
169 
170     *pixels =
171         (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
172                   rect->x * surface->format->BytesPerPixel);
173     *pitch = surface->pitch;
174     return 0;
175 }
176 
177 static void
SW_UnlockTexture(SDL_Renderer * renderer,SDL_Texture * texture)178 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
179 {
180 }
181 
182 static void
SW_SetTextureScaleMode(SDL_Renderer * renderer,SDL_Texture * texture,SDL_ScaleMode scaleMode)183 SW_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
184 {
185 }
186 
187 static int
SW_SetRenderTarget(SDL_Renderer * renderer,SDL_Texture * texture)188 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
189 {
190     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
191 
192     if (texture) {
193         data->surface = (SDL_Surface *) texture->driverdata;
194     } else {
195         data->surface = data->window;
196     }
197     return 0;
198 }
199 
200 static int
SW_QueueSetViewport(SDL_Renderer * renderer,SDL_RenderCommand * cmd)201 SW_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
202 {
203     return 0;  /* nothing to do in this backend. */
204 }
205 
206 static int
SW_QueueDrawPoints(SDL_Renderer * renderer,SDL_RenderCommand * cmd,const SDL_FPoint * points,int count)207 SW_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
208 {
209     SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first);
210     int i;
211 
212     if (!verts) {
213         return -1;
214     }
215 
216     cmd->data.draw.count = count;
217 
218     if (renderer->viewport.x || renderer->viewport.y) {
219         const int x = renderer->viewport.x;
220         const int y = renderer->viewport.y;
221         for (i = 0; i < count; i++, verts++, points++) {
222             verts->x = (int)(x + points->x);
223             verts->y = (int)(y + points->y);
224         }
225     } else {
226         for (i = 0; i < count; i++, verts++, points++) {
227             verts->x = (int)points->x;
228             verts->y = (int)points->y;
229         }
230     }
231 
232     return 0;
233 }
234 
235 static int
SW_QueueFillRects(SDL_Renderer * renderer,SDL_RenderCommand * cmd,const SDL_FRect * rects,int count)236 SW_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
237 {
238     SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
239     int i;
240 
241     if (!verts) {
242         return -1;
243     }
244 
245     cmd->data.draw.count = count;
246 
247     if (renderer->viewport.x || renderer->viewport.y) {
248         const int x = renderer->viewport.x;
249         const int y = renderer->viewport.y;
250 
251         for (i = 0; i < count; i++, verts++, rects++) {
252             verts->x = (int)(x + rects->x);
253             verts->y = (int)(y + rects->y);
254             verts->w = SDL_max((int)rects->w, 1);
255             verts->h = SDL_max((int)rects->h, 1);
256         }
257     } else {
258         for (i = 0; i < count; i++, verts++, rects++) {
259             verts->x = (int)rects->x;
260             verts->y = (int)rects->y;
261             verts->w = SDL_max((int)rects->w, 1);
262             verts->h = SDL_max((int)rects->h, 1);
263         }
264     }
265 
266     return 0;
267 }
268 
269 static int
SW_QueueCopy(SDL_Renderer * renderer,SDL_RenderCommand * cmd,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect)270 SW_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
271              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
272 {
273     SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
274 
275     if (!verts) {
276         return -1;
277     }
278 
279     cmd->data.draw.count = 1;
280 
281     SDL_memcpy(verts, srcrect, sizeof (SDL_Rect));
282     verts++;
283 
284     if (renderer->viewport.x || renderer->viewport.y) {
285         verts->x = (int)(renderer->viewport.x + dstrect->x);
286         verts->y = (int)(renderer->viewport.y + dstrect->y);
287     } else {
288         verts->x = (int)dstrect->x;
289         verts->y = (int)dstrect->y;
290     }
291     verts->w = (int)dstrect->w;
292     verts->h = (int)dstrect->h;
293 
294     return 0;
295 }
296 
297 typedef struct CopyExData
298 {
299     SDL_Rect srcrect;
300     SDL_Rect dstrect;
301     double angle;
302     SDL_FPoint center;
303     SDL_RendererFlip flip;
304 } CopyExData;
305 
306 static int
SW_QueueCopyEx(SDL_Renderer * renderer,SDL_RenderCommand * cmd,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)307 SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
308                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
309                const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
310 {
311     CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first);
312 
313     if (!verts) {
314         return -1;
315     }
316 
317     cmd->data.draw.count = 1;
318 
319     SDL_memcpy(&verts->srcrect, srcrect, sizeof (SDL_Rect));
320 
321     if (renderer->viewport.x || renderer->viewport.y) {
322         verts->dstrect.x = (int)(renderer->viewport.x + dstrect->x);
323         verts->dstrect.y = (int)(renderer->viewport.y + dstrect->y);
324     } else {
325         verts->dstrect.x = (int)dstrect->x;
326         verts->dstrect.y = (int)dstrect->y;
327     }
328     verts->dstrect.w = (int)dstrect->w;
329     verts->dstrect.h = (int)dstrect->h;
330     verts->angle = angle;
331     SDL_memcpy(&verts->center, center, sizeof (SDL_FPoint));
332     verts->flip = flip;
333 
334     return 0;
335 }
336 
337 static int
SW_RenderCopyEx(SDL_Renderer * renderer,SDL_Surface * surface,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_Rect * final_rect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)338 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture,
339                 const SDL_Rect * srcrect, const SDL_Rect * final_rect,
340                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
341 {
342     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
343     SDL_Rect tmp_rect;
344     SDL_Surface *src_clone, *src_rotated, *src_scaled;
345     SDL_Surface *mask = NULL, *mask_rotated = NULL;
346     int retval = 0, dstwidth, dstheight, abscenterx, abscentery;
347     double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
348     SDL_BlendMode blendmode;
349     Uint8 alphaMod, rMod, gMod, bMod;
350     int applyModulation = SDL_FALSE;
351     int blitRequired = SDL_FALSE;
352     int isOpaque = SDL_FALSE;
353 
354     if (!surface) {
355         return -1;
356     }
357 
358     tmp_rect.x = 0;
359     tmp_rect.y = 0;
360     tmp_rect.w = final_rect->w;
361     tmp_rect.h = final_rect->h;
362 
363     /* It is possible to encounter an RLE encoded surface here and locking it is
364      * necessary because this code is going to access the pixel buffer directly.
365      */
366     if (SDL_MUSTLOCK(src)) {
367         SDL_LockSurface(src);
368     }
369 
370     /* Clone the source surface but use its pixel buffer directly.
371      * The original source surface must be treated as read-only.
372      */
373     src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch,
374                                          src->format->Rmask, src->format->Gmask,
375                                          src->format->Bmask, src->format->Amask);
376     if (src_clone == NULL) {
377         if (SDL_MUSTLOCK(src)) {
378             SDL_UnlockSurface(src);
379         }
380         return -1;
381     }
382 
383     SDL_GetSurfaceBlendMode(src, &blendmode);
384     SDL_GetSurfaceAlphaMod(src, &alphaMod);
385     SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
386 
387     /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */
388     if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) {
389         blitRequired = SDL_TRUE;
390     }
391 
392     /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */
393     if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) {
394         blitRequired = SDL_TRUE;
395     }
396 
397     /* srcrect is not selecting the whole src surface, so cropping is needed */
398     if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
399         blitRequired = SDL_TRUE;
400     }
401 
402     /* The color and alpha modulation has to be applied before the rotation when using the NONE, MOD or MUL blend modes. */
403     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) && (alphaMod & rMod & gMod & bMod) != 255) {
404         applyModulation = SDL_TRUE;
405         SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
406         SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
407     }
408 
409     /* Opaque surfaces are much easier to handle with the NONE blend mode. */
410     if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
411         isOpaque = SDL_TRUE;
412     }
413 
414     /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
415      * to clear the pixels in the destination surface. The other steps are explained below.
416      */
417     if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
418         mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
419                                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
420         if (mask == NULL) {
421             retval = -1;
422         } else {
423             SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
424         }
425     }
426 
427     /* Create a new surface should there be a format mismatch or if scaling, cropping,
428      * or modulation is required. It's possible to use the source surface directly otherwise.
429      */
430     if (!retval && (blitRequired || applyModulation)) {
431         SDL_Rect scale_rect = tmp_rect;
432         src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
433                                           0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
434         if (src_scaled == NULL) {
435             retval = -1;
436         } else {
437             SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
438             retval = SDL_BlitScaled(src_clone, srcrect, src_scaled, &scale_rect);
439             SDL_FreeSurface(src_clone);
440             src_clone = src_scaled;
441             src_scaled = NULL;
442         }
443     }
444 
445     /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
446     SDL_SetSurfaceBlendMode(src_clone, blendmode);
447 
448     if (!retval) {
449         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
450         src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, (texture->scaleMode == SDL_ScaleModeNearest) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
451         if (src_rotated == NULL) {
452             retval = -1;
453         }
454         if (!retval && mask != NULL) {
455             /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
456             mask_rotated = SDLgfx_rotateSurface(mask, angle, dstwidth/2, dstheight/2, SDL_FALSE, 0, 0, dstwidth, dstheight, cangle, sangle);
457             if (mask_rotated == NULL) {
458                 retval = -1;
459             }
460         }
461         if (!retval) {
462             /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
463             abscenterx = final_rect->x + (int)center->x;
464             abscentery = final_rect->y + (int)center->y;
465             /* Compensate the angle inversion to match the behaviour of the other backends */
466             sangle = -sangle;
467 
468             /* Top Left */
469             px = final_rect->x - abscenterx;
470             py = final_rect->y - abscentery;
471             p1x = px * cangle - py * sangle + abscenterx;
472             p1y = px * sangle + py * cangle + abscentery;
473 
474             /* Top Right */
475             px = final_rect->x + final_rect->w - abscenterx;
476             py = final_rect->y - abscentery;
477             p2x = px * cangle - py * sangle + abscenterx;
478             p2y = px * sangle + py * cangle + abscentery;
479 
480             /* Bottom Left */
481             px = final_rect->x - abscenterx;
482             py = final_rect->y + final_rect->h - abscentery;
483             p3x = px * cangle - py * sangle + abscenterx;
484             p3y = px * sangle + py * cangle + abscentery;
485 
486             /* Bottom Right */
487             px = final_rect->x + final_rect->w - abscenterx;
488             py = final_rect->y + final_rect->h - abscentery;
489             p4x = px * cangle - py * sangle + abscenterx;
490             p4y = px * sangle + py * cangle + abscentery;
491 
492             tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
493             tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
494             tmp_rect.w = dstwidth;
495             tmp_rect.h = dstheight;
496 
497             /* The NONE blend mode needs some special care with non-opaque surfaces.
498              * Other blend modes or opaque surfaces can be blitted directly.
499              */
500             if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
501                 if (applyModulation == SDL_FALSE) {
502                     /* If the modulation wasn't already applied, make it happen now. */
503                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
504                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
505                 }
506                 retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
507             } else {
508                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
509                  * First, the area where the rotated pixels will be blitted to get set to zero.
510                  * This is accomplished by simply blitting a mask with the NONE blend mode.
511                  * The colorkey set by the rotate function will discard the correct pixels.
512                  */
513                 SDL_Rect mask_rect = tmp_rect;
514                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
515                 retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
516                 if (!retval) {
517                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
518                      * by modulating the source colors with 0. Since the destination is all zeros, this
519                      * will effectively set the destination alpha to the source alpha.
520                      */
521                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
522                     mask_rect = tmp_rect;
523                     retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
524                     if (!retval) {
525                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
526                          * the destination (where the color values are all zero). However, because the ADD blend
527                          * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
528                          * to be created. This makes all source pixels opaque and the colors get copied correctly.
529                          */
530                         SDL_Surface *src_rotated_rgb;
531                         src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
532                                                                    src_rotated->format->BitsPerPixel, src_rotated->pitch,
533                                                                    src_rotated->format->Rmask, src_rotated->format->Gmask,
534                                                                    src_rotated->format->Bmask, 0);
535                         if (src_rotated_rgb == NULL) {
536                             retval = -1;
537                         } else {
538                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
539                             retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
540                             SDL_FreeSurface(src_rotated_rgb);
541                         }
542                     }
543                 }
544                 SDL_FreeSurface(mask_rotated);
545             }
546             if (src_rotated != NULL) {
547                 SDL_FreeSurface(src_rotated);
548             }
549         }
550     }
551 
552     if (SDL_MUSTLOCK(src)) {
553         SDL_UnlockSurface(src);
554     }
555     if (mask != NULL) {
556         SDL_FreeSurface(mask);
557     }
558     if (src_clone != NULL) {
559         SDL_FreeSurface(src_clone);
560     }
561     return retval;
562 }
563 
564 static void
PrepTextureForCopy(const SDL_RenderCommand * cmd)565 PrepTextureForCopy(const SDL_RenderCommand *cmd)
566 {
567     const Uint8 r = cmd->data.draw.r;
568     const Uint8 g = cmd->data.draw.g;
569     const Uint8 b = cmd->data.draw.b;
570     const Uint8 a = cmd->data.draw.a;
571     const SDL_BlendMode blend = cmd->data.draw.blend;
572     SDL_Texture *texture = cmd->data.draw.texture;
573     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
574     const SDL_bool colormod = ((r & g & b) != 0xFF);
575     const SDL_bool alphamod = (a != 0xFF);
576     const SDL_bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD) || (blend == SDL_BLENDMODE_MUL));
577 
578     if (colormod || alphamod || blending) {
579         SDL_SetSurfaceRLE(surface, 0);
580     }
581 
582     /* !!! FIXME: we can probably avoid some of these calls. */
583     SDL_SetSurfaceColorMod(surface, r, g, b);
584     SDL_SetSurfaceAlphaMod(surface, a);
585     SDL_SetSurfaceBlendMode(surface, blend);
586 }
587 
588 static void
SetDrawState(SDL_Surface * surface,SW_DrawStateCache * drawstate)589 SetDrawState(SDL_Surface *surface, SW_DrawStateCache *drawstate)
590 {
591     if (drawstate->surface_cliprect_dirty) {
592         const SDL_Rect *viewport = drawstate->viewport;
593         const SDL_Rect *cliprect = drawstate->cliprect;
594         SDL_assert(viewport != NULL);  /* the higher level should have forced a SDL_RENDERCMD_SETVIEWPORT */
595 
596         if (cliprect != NULL) {
597             SDL_Rect clip_rect;
598             clip_rect.x = cliprect->x + viewport->x;
599             clip_rect.y = cliprect->y + viewport->y;
600             clip_rect.w = cliprect->w;
601             clip_rect.h = cliprect->h;
602             SDL_IntersectRect(viewport, &clip_rect, &clip_rect);
603             SDL_SetClipRect(surface, &clip_rect);
604         } else {
605             SDL_SetClipRect(surface, drawstate->viewport);
606         }
607         drawstate->surface_cliprect_dirty = SDL_FALSE;
608     }
609 }
610 
611 static int
SW_RunCommandQueue(SDL_Renderer * renderer,SDL_RenderCommand * cmd,void * vertices,size_t vertsize)612 SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
613 {
614     SDL_Surface *surface = SW_ActivateRenderer(renderer);
615     SW_DrawStateCache drawstate;
616 
617     if (!surface) {
618         return -1;
619     }
620 
621     drawstate.viewport = NULL;
622     drawstate.cliprect = NULL;
623     drawstate.surface_cliprect_dirty = SDL_TRUE;
624 
625     while (cmd) {
626         switch (cmd->command) {
627             case SDL_RENDERCMD_SETDRAWCOLOR: {
628                 break;  /* Not used in this backend. */
629             }
630 
631             case SDL_RENDERCMD_SETVIEWPORT: {
632                 drawstate.viewport = &cmd->data.viewport.rect;
633                 drawstate.surface_cliprect_dirty = SDL_TRUE;
634                 break;
635             }
636 
637             case SDL_RENDERCMD_SETCLIPRECT: {
638                 drawstate.cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
639                 drawstate.surface_cliprect_dirty = SDL_TRUE;
640                 break;
641             }
642 
643             case SDL_RENDERCMD_CLEAR: {
644                 const Uint8 r = cmd->data.color.r;
645                 const Uint8 g = cmd->data.color.g;
646                 const Uint8 b = cmd->data.color.b;
647                 const Uint8 a = cmd->data.color.a;
648                 /* By definition the clear ignores the clip rect */
649                 SDL_SetClipRect(surface, NULL);
650                 SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a));
651                 drawstate.surface_cliprect_dirty = SDL_TRUE;
652                 break;
653             }
654 
655             case SDL_RENDERCMD_DRAW_POINTS: {
656                 const Uint8 r = cmd->data.draw.r;
657                 const Uint8 g = cmd->data.draw.g;
658                 const Uint8 b = cmd->data.draw.b;
659                 const Uint8 a = cmd->data.draw.a;
660                 const int count = (int) cmd->data.draw.count;
661                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
662                 const SDL_BlendMode blend = cmd->data.draw.blend;
663                 SetDrawState(surface, &drawstate);
664                 if (blend == SDL_BLENDMODE_NONE) {
665                     SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
666                 } else {
667                     SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
668                 }
669                 break;
670             }
671 
672             case SDL_RENDERCMD_DRAW_LINES: {
673                 const Uint8 r = cmd->data.draw.r;
674                 const Uint8 g = cmd->data.draw.g;
675                 const Uint8 b = cmd->data.draw.b;
676                 const Uint8 a = cmd->data.draw.a;
677                 const int count = (int) cmd->data.draw.count;
678                 const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
679                 const SDL_BlendMode blend = cmd->data.draw.blend;
680                 SetDrawState(surface, &drawstate);
681                 if (blend == SDL_BLENDMODE_NONE) {
682                     SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
683                 } else {
684                     SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
685                 }
686                 break;
687             }
688 
689             case SDL_RENDERCMD_FILL_RECTS: {
690                 const Uint8 r = cmd->data.draw.r;
691                 const Uint8 g = cmd->data.draw.g;
692                 const Uint8 b = cmd->data.draw.b;
693                 const Uint8 a = cmd->data.draw.a;
694                 const int count = (int) cmd->data.draw.count;
695                 const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
696                 const SDL_BlendMode blend = cmd->data.draw.blend;
697                 SetDrawState(surface, &drawstate);
698                 if (blend == SDL_BLENDMODE_NONE) {
699                     SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
700                 } else {
701                     SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
702                 }
703                 break;
704             }
705 
706             case SDL_RENDERCMD_COPY: {
707                 SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
708                 const SDL_Rect *srcrect = verts;
709                 SDL_Rect *dstrect = verts + 1;
710                 SDL_Texture *texture = cmd->data.draw.texture;
711                 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
712 
713                 SetDrawState(surface, &drawstate);
714 
715                 PrepTextureForCopy(cmd);
716 
717                 if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) {
718                     SDL_BlitSurface(src, srcrect, surface, dstrect);
719                 } else {
720                     /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
721                      * to avoid potentially frequent RLE encoding/decoding.
722                      */
723                     SDL_SetSurfaceRLE(surface, 0);
724                     SDL_BlitScaled(src, srcrect, surface, dstrect);
725                 }
726                 break;
727             }
728 
729             case SDL_RENDERCMD_COPY_EX: {
730                 const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first);
731                 SetDrawState(surface, &drawstate);
732                 PrepTextureForCopy(cmd);
733                 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
734                                 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip);
735                 break;
736             }
737 
738             case SDL_RENDERCMD_NO_OP:
739                 break;
740         }
741 
742         cmd = cmd->next;
743     }
744 
745     return 0;
746 }
747 
748 static int
SW_RenderReadPixels(SDL_Renderer * renderer,const SDL_Rect * rect,Uint32 format,void * pixels,int pitch)749 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
750                     Uint32 format, void * pixels, int pitch)
751 {
752     SDL_Surface *surface = SW_ActivateRenderer(renderer);
753     Uint32 src_format;
754     void *src_pixels;
755 
756     if (!surface) {
757         return -1;
758     }
759 
760     /* NOTE: The rect is already adjusted according to the viewport by
761      * SDL_RenderReadPixels.
762      */
763 
764     if (rect->x < 0 || rect->x+rect->w > surface->w ||
765         rect->y < 0 || rect->y+rect->h > surface->h) {
766         return SDL_SetError("Tried to read outside of surface bounds");
767     }
768 
769     src_format = surface->format->format;
770     src_pixels = (void*)((Uint8 *) surface->pixels +
771                     rect->y * surface->pitch +
772                     rect->x * surface->format->BytesPerPixel);
773 
774     return SDL_ConvertPixels(rect->w, rect->h,
775                              src_format, src_pixels, surface->pitch,
776                              format, pixels, pitch);
777 }
778 
779 static void
SW_RenderPresent(SDL_Renderer * renderer)780 SW_RenderPresent(SDL_Renderer * renderer)
781 {
782     SDL_Window *window = renderer->window;
783 
784     if (window) {
785         SDL_UpdateWindowSurface(window);
786     }
787 }
788 
789 static void
SW_DestroyTexture(SDL_Renderer * renderer,SDL_Texture * texture)790 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
791 {
792     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
793 
794     SDL_FreeSurface(surface);
795 }
796 
797 static void
SW_DestroyRenderer(SDL_Renderer * renderer)798 SW_DestroyRenderer(SDL_Renderer * renderer)
799 {
800     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
801 
802     SDL_free(data);
803     SDL_free(renderer);
804 }
805 
806 SDL_Renderer *
SW_CreateRendererForSurface(SDL_Surface * surface)807 SW_CreateRendererForSurface(SDL_Surface * surface)
808 {
809     SDL_Renderer *renderer;
810     SW_RenderData *data;
811 
812     if (!surface) {
813         SDL_SetError("Can't create renderer for NULL surface");
814         return NULL;
815     }
816 
817     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
818     if (!renderer) {
819         SDL_OutOfMemory();
820         return NULL;
821     }
822 
823     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
824     if (!data) {
825         SW_DestroyRenderer(renderer);
826         SDL_OutOfMemory();
827         return NULL;
828     }
829     data->surface = surface;
830     data->window = surface;
831 
832     renderer->WindowEvent = SW_WindowEvent;
833     renderer->GetOutputSize = SW_GetOutputSize;
834     renderer->CreateTexture = SW_CreateTexture;
835     renderer->UpdateTexture = SW_UpdateTexture;
836     renderer->LockTexture = SW_LockTexture;
837     renderer->UnlockTexture = SW_UnlockTexture;
838     renderer->SetTextureScaleMode = SW_SetTextureScaleMode;
839     renderer->SetRenderTarget = SW_SetRenderTarget;
840     renderer->QueueSetViewport = SW_QueueSetViewport;
841     renderer->QueueSetDrawColor = SW_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
842     renderer->QueueDrawPoints = SW_QueueDrawPoints;
843     renderer->QueueDrawLines = SW_QueueDrawPoints;  /* lines and points queue vertices the same way. */
844     renderer->QueueFillRects = SW_QueueFillRects;
845     renderer->QueueCopy = SW_QueueCopy;
846     renderer->QueueCopyEx = SW_QueueCopyEx;
847     renderer->RunCommandQueue = SW_RunCommandQueue;
848     renderer->RenderReadPixels = SW_RenderReadPixels;
849     renderer->RenderPresent = SW_RenderPresent;
850     renderer->DestroyTexture = SW_DestroyTexture;
851     renderer->DestroyRenderer = SW_DestroyRenderer;
852     renderer->info = SW_RenderDriver.info;
853     renderer->driverdata = data;
854 
855     SW_ActivateRenderer(renderer);
856 
857     return renderer;
858 }
859 
860 static SDL_Renderer *
SW_CreateRenderer(SDL_Window * window,Uint32 flags)861 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
862 {
863     SDL_Surface *surface;
864 
865     surface = SDL_GetWindowSurface(window);
866     if (!surface) {
867         return NULL;
868     }
869     return SW_CreateRendererForSurface(surface);
870 }
871 
872 SDL_RenderDriver SW_RenderDriver = {
873     SW_CreateRenderer,
874     {
875      "software",
876      SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
877      8,
878      {
879       SDL_PIXELFORMAT_ARGB8888,
880       SDL_PIXELFORMAT_ABGR8888,
881       SDL_PIXELFORMAT_RGBA8888,
882       SDL_PIXELFORMAT_BGRA8888,
883       SDL_PIXELFORMAT_RGB888,
884       SDL_PIXELFORMAT_BGR888,
885       SDL_PIXELFORMAT_RGB565,
886       SDL_PIXELFORMAT_RGB555
887      },
888      0,
889      0}
890 };
891 
892 #endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
893 
894 /* vi: set ts=4 sw=4 expandtab: */
895