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