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, ©data->srcrect,
734 ©data->dstrect, copydata->angle, ©data->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