summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c')
-rw-r--r--contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c b/contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c
new file mode 100644
index 0000000..2231724
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/render/software/SDL_render_sw.c
@@ -0,0 +1,1202 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#ifdef SDL_VIDEO_RENDER_SW
24
25#include "../SDL_sysrender.h"
26#include "SDL_render_sw_c.h"
27
28#include "SDL_draw.h"
29#include "SDL_blendfillrect.h"
30#include "SDL_blendline.h"
31#include "SDL_blendpoint.h"
32#include "SDL_drawline.h"
33#include "SDL_drawpoint.h"
34#include "SDL_rotate.h"
35#include "SDL_triangle.h"
36#include "../../video/SDL_pixels_c.h"
37
38// SDL surface based renderer implementation
39
40typedef struct
41{
42 const SDL_Rect *viewport;
43 const SDL_Rect *cliprect;
44 bool surface_cliprect_dirty;
45 SDL_Color color;
46} SW_DrawStateCache;
47
48typedef struct
49{
50 SDL_Surface *surface;
51 SDL_Surface *window;
52} SW_RenderData;
53
54static SDL_Surface *SW_ActivateRenderer(SDL_Renderer *renderer)
55{
56 SW_RenderData *data = (SW_RenderData *)renderer->internal;
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 SW_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
71{
72 SW_RenderData *data = (SW_RenderData *)renderer->internal;
73
74 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
75 data->surface = NULL;
76 data->window = NULL;
77 }
78}
79
80static bool SW_GetOutputSize(SDL_Renderer *renderer, int *w, int *h)
81{
82 SW_RenderData *data = (SW_RenderData *)renderer->internal;
83
84 if (data->surface) {
85 if (w) {
86 *w = data->surface->w;
87 }
88 if (h) {
89 *h = data->surface->h;
90 }
91 return true;
92 }
93
94 if (renderer->window) {
95 SDL_GetWindowSizeInPixels(renderer->window, w, h);
96 return true;
97 }
98
99 return SDL_SetError("Software renderer doesn't have an output surface");
100}
101
102static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
103{
104 SDL_Surface *surface = SDL_CreateSurface(texture->w, texture->h, texture->format);
105 Uint8 r, g, b, a;
106
107 if (!SDL_SurfaceValid(surface)) {
108 return SDL_SetError("Cannot create surface");
109 }
110 texture->internal = surface;
111 r = (Uint8)SDL_roundf(SDL_clamp(texture->color.r, 0.0f, 1.0f) * 255.0f);
112 g = (Uint8)SDL_roundf(SDL_clamp(texture->color.g, 0.0f, 1.0f) * 255.0f);
113 b = (Uint8)SDL_roundf(SDL_clamp(texture->color.b, 0.0f, 1.0f) * 255.0f);
114 a = (Uint8)SDL_roundf(SDL_clamp(texture->color.a, 0.0f, 1.0f) * 255.0f);
115 SDL_SetSurfaceColorMod(surface, r, g, b);
116 SDL_SetSurfaceAlphaMod(surface, a);
117 SDL_SetSurfaceBlendMode(surface, texture->blendMode);
118
119 /* Only RLE encode textures without an alpha channel since the RLE coder
120 * discards the color values of pixels with an alpha value of zero.
121 */
122 if (texture->access == SDL_TEXTUREACCESS_STATIC && !SDL_ISPIXELFORMAT_ALPHA(surface->format)) {
123 SDL_SetSurfaceRLE(surface, 1);
124 }
125
126 return true;
127}
128
129static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
130 const SDL_Rect *rect, const void *pixels, int pitch)
131{
132 SDL_Surface *surface = (SDL_Surface *)texture->internal;
133 Uint8 *src, *dst;
134 int row;
135 size_t length;
136
137 if (SDL_MUSTLOCK(surface)) {
138 if (!SDL_LockSurface(surface)) {
139 return false;
140 }
141 }
142 src = (Uint8 *)pixels;
143 dst = (Uint8 *)surface->pixels +
144 rect->y * surface->pitch +
145 rect->x * surface->fmt->bytes_per_pixel;
146 length = (size_t)rect->w * surface->fmt->bytes_per_pixel;
147 for (row = 0; row < rect->h; ++row) {
148 SDL_memcpy(dst, src, length);
149 src += pitch;
150 dst += surface->pitch;
151 }
152 if (SDL_MUSTLOCK(surface)) {
153 SDL_UnlockSurface(surface);
154 }
155 return true;
156}
157
158static bool SW_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
159 const SDL_Rect *rect, void **pixels, int *pitch)
160{
161 SDL_Surface *surface = (SDL_Surface *)texture->internal;
162
163 *pixels =
164 (void *)((Uint8 *)surface->pixels + rect->y * surface->pitch +
165 rect->x * surface->fmt->bytes_per_pixel);
166 *pitch = surface->pitch;
167 return true;
168}
169
170static void SW_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
171{
172}
173
174static void SW_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode)
175{
176}
177
178static bool SW_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
179{
180 SW_RenderData *data = (SW_RenderData *)renderer->internal;
181
182 if (texture) {
183 data->surface = (SDL_Surface *)texture->internal;
184 } else {
185 data->surface = data->window;
186 }
187 return true;
188}
189
190static bool SW_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
191{
192 return true; // nothing to do in this backend.
193}
194
195static bool SW_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
196{
197 SDL_Point *verts = (SDL_Point *)SDL_AllocateRenderVertices(renderer, count * sizeof(SDL_Point), 0, &cmd->data.draw.first);
198 int i;
199
200 if (!verts) {
201 return false;
202 }
203
204 cmd->data.draw.count = count;
205
206 for (i = 0; i < count; i++, verts++, points++) {
207 verts->x = (int)points->x;
208 verts->y = (int)points->y;
209 }
210
211 return true;
212}
213
214static bool SW_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FRect *rects, int count)
215{
216 SDL_Rect *verts = (SDL_Rect *)SDL_AllocateRenderVertices(renderer, count * sizeof(SDL_Rect), 0, &cmd->data.draw.first);
217 int i;
218
219 if (!verts) {
220 return false;
221 }
222
223 cmd->data.draw.count = count;
224
225 for (i = 0; i < count; i++, verts++, rects++) {
226 verts->x = (int)rects->x;
227 verts->y = (int)rects->y;
228 verts->w = SDL_max((int)rects->w, 1);
229 verts->h = SDL_max((int)rects->h, 1);
230 }
231
232 return true;
233}
234
235static bool SW_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
236 const SDL_FRect *srcrect, const SDL_FRect *dstrect)
237{
238 SDL_Rect *verts = (SDL_Rect *)SDL_AllocateRenderVertices(renderer, 2 * sizeof(SDL_Rect), 0, &cmd->data.draw.first);
239
240 if (!verts) {
241 return false;
242 }
243
244 cmd->data.draw.count = 1;
245
246 verts->x = (int)srcrect->x;
247 verts->y = (int)srcrect->y;
248 verts->w = (int)srcrect->w;
249 verts->h = (int)srcrect->h;
250 verts++;
251
252 verts->x = (int)dstrect->x;
253 verts->y = (int)dstrect->y;
254 verts->w = (int)dstrect->w;
255 verts->h = (int)dstrect->h;
256
257 return true;
258}
259
260typedef struct CopyExData
261{
262 SDL_Rect srcrect;
263 SDL_Rect dstrect;
264 double angle;
265 SDL_FPoint center;
266 SDL_FlipMode flip;
267 float scale_x;
268 float scale_y;
269} CopyExData;
270
271static bool SW_QueueCopyEx(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
272 const SDL_FRect *srcrect, const SDL_FRect *dstrect,
273 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y)
274{
275 CopyExData *verts = (CopyExData *)SDL_AllocateRenderVertices(renderer, sizeof(CopyExData), 0, &cmd->data.draw.first);
276
277 if (!verts) {
278 return false;
279 }
280
281 cmd->data.draw.count = 1;
282
283 verts->srcrect.x = (int)srcrect->x;
284 verts->srcrect.y = (int)srcrect->y;
285 verts->srcrect.w = (int)srcrect->w;
286 verts->srcrect.h = (int)srcrect->h;
287 verts->dstrect.x = (int)dstrect->x;
288 verts->dstrect.y = (int)dstrect->y;
289 verts->dstrect.w = (int)dstrect->w;
290 verts->dstrect.h = (int)dstrect->h;
291 verts->angle = angle;
292 SDL_copyp(&verts->center, center);
293 verts->flip = flip;
294 verts->scale_x = scale_x;
295 verts->scale_y = scale_y;
296
297 return true;
298}
299
300static bool Blit_to_Screen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *surface, SDL_Rect *dstrect,
301 float scale_x, float scale_y, SDL_ScaleMode scaleMode)
302{
303 bool result;
304 // Renderer scaling, if needed
305 if (scale_x != 1.0f || scale_y != 1.0f) {
306 SDL_Rect r;
307 r.x = (int)((float)dstrect->x * scale_x);
308 r.y = (int)((float)dstrect->y * scale_y);
309 r.w = (int)((float)dstrect->w * scale_x);
310 r.h = (int)((float)dstrect->h * scale_y);
311 result = SDL_BlitSurfaceScaled(src, srcrect, surface, &r, scaleMode);
312 } else {
313 result = SDL_BlitSurface(src, srcrect, surface, dstrect);
314 }
315 return result;
316}
317
318static bool SW_RenderCopyEx(SDL_Renderer *renderer, SDL_Surface *surface, SDL_Texture *texture,
319 const SDL_Rect *srcrect, const SDL_Rect *final_rect,
320 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y)
321{
322 SDL_Surface *src = (SDL_Surface *)texture->internal;
323 SDL_Rect tmp_rect;
324 SDL_Surface *src_clone, *src_rotated, *src_scaled;
325 SDL_Surface *mask = NULL, *mask_rotated = NULL;
326 bool result = true;
327 SDL_BlendMode blendmode;
328 Uint8 alphaMod, rMod, gMod, bMod;
329 int applyModulation = false;
330 int blitRequired = false;
331 int isOpaque = false;
332
333 if (!SDL_SurfaceValid(surface)) {
334 return false;
335 }
336
337 tmp_rect.x = 0;
338 tmp_rect.y = 0;
339 tmp_rect.w = final_rect->w;
340 tmp_rect.h = final_rect->h;
341
342 /* It is possible to encounter an RLE encoded surface here and locking it is
343 * necessary because this code is going to access the pixel buffer directly.
344 */
345 if (SDL_MUSTLOCK(src)) {
346 if (!SDL_LockSurface(src)) {
347 return false;
348 }
349 }
350
351 /* Clone the source surface but use its pixel buffer directly.
352 * The original source surface must be treated as read-only.
353 */
354 src_clone = SDL_CreateSurfaceFrom(src->w, src->h, src->format, src->pixels, src->pitch);
355 if (!src_clone) {
356 if (SDL_MUSTLOCK(src)) {
357 SDL_UnlockSurface(src);
358 }
359 return false;
360 }
361
362 SDL_GetSurfaceBlendMode(src, &blendmode);
363 SDL_GetSurfaceAlphaMod(src, &alphaMod);
364 SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
365
366 // SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted.
367 if (src->fmt->bits_per_pixel != 32 || SDL_PIXELLAYOUT(src->format) != SDL_PACKEDLAYOUT_8888 || !SDL_ISPIXELFORMAT_ALPHA(src->format)) {
368 blitRequired = true;
369 }
370
371 // If scaling and cropping is necessary, it has to be taken care of before the rotation.
372 if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) {
373 blitRequired = true;
374 }
375
376 // srcrect is not selecting the whole src surface, so cropping is needed
377 if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
378 blitRequired = true;
379 }
380
381 // The color and alpha modulation has to be applied before the rotation when using the NONE, MOD or MUL blend modes.
382 if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) && (alphaMod & rMod & gMod & bMod) != 255) {
383 applyModulation = true;
384 SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
385 SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
386 }
387
388 // Opaque surfaces are much easier to handle with the NONE blend mode.
389 if (blendmode == SDL_BLENDMODE_NONE && !SDL_ISPIXELFORMAT_ALPHA(src->format) && alphaMod == 255) {
390 isOpaque = true;
391 }
392
393 /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
394 * to clear the pixels in the destination surface. The other steps are explained below.
395 */
396 if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
397 mask = SDL_CreateSurface(final_rect->w, final_rect->h, SDL_PIXELFORMAT_ARGB8888);
398 if (!mask) {
399 result = false;
400 } else {
401 SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
402 }
403 }
404
405 /* Create a new surface should there be a format mismatch or if scaling, cropping,
406 * or modulation is required. It's possible to use the source surface directly otherwise.
407 */
408 if (result && (blitRequired || applyModulation)) {
409 SDL_Rect scale_rect = tmp_rect;
410 src_scaled = SDL_CreateSurface(final_rect->w, final_rect->h, SDL_PIXELFORMAT_ARGB8888);
411 if (!src_scaled) {
412 result = false;
413 } else {
414 SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
415 result = SDL_BlitSurfaceScaled(src_clone, srcrect, src_scaled, &scale_rect, texture->scaleMode);
416 SDL_DestroySurface(src_clone);
417 src_clone = src_scaled;
418 src_scaled = NULL;
419 }
420 }
421
422 // SDLgfx_rotateSurface is going to make decisions depending on the blend mode.
423 SDL_SetSurfaceBlendMode(src_clone, blendmode);
424
425 if (result) {
426 SDL_Rect rect_dest;
427 double cangle, sangle;
428
429 SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, center,
430 &rect_dest, &cangle, &sangle);
431 src_rotated = SDLgfx_rotateSurface(src_clone, angle,
432 (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL,
433 &rect_dest, cangle, sangle, center);
434 if (!src_rotated) {
435 result = false;
436 }
437 if (result && mask) {
438 // The mask needed for the NONE blend mode gets rotated with the same parameters.
439 mask_rotated = SDLgfx_rotateSurface(mask, angle,
440 false, 0, 0,
441 &rect_dest, cangle, sangle, center);
442 if (!mask_rotated) {
443 result = false;
444 }
445 }
446 if (result) {
447
448 tmp_rect.x = final_rect->x + rect_dest.x;
449 tmp_rect.y = final_rect->y + rect_dest.y;
450 tmp_rect.w = rect_dest.w;
451 tmp_rect.h = rect_dest.h;
452
453 /* The NONE blend mode needs some special care with non-opaque surfaces.
454 * Other blend modes or opaque surfaces can be blitted directly.
455 */
456 if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
457 if (applyModulation == false) {
458 // If the modulation wasn't already applied, make it happen now.
459 SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
460 SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
461 }
462 // Renderer scaling, if needed
463 result = Blit_to_Screen(src_rotated, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
464 } else {
465 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
466 * First, the area where the rotated pixels will be blitted to get set to zero.
467 * This is accomplished by simply blitting a mask with the NONE blend mode.
468 * The colorkey set by the rotate function will discard the correct pixels.
469 */
470 SDL_Rect mask_rect = tmp_rect;
471 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
472 // Renderer scaling, if needed
473 result = Blit_to_Screen(mask_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
474 if (result) {
475 /* The next step copies the alpha value. This is done with the BLEND blend mode and
476 * by modulating the source colors with 0. Since the destination is all zeros, this
477 * will effectively set the destination alpha to the source alpha.
478 */
479 SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
480 mask_rect = tmp_rect;
481 // Renderer scaling, if needed
482 result = Blit_to_Screen(src_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
483 if (result) {
484 /* The last step gets the color values in place. The ADD blend mode simply adds them to
485 * the destination (where the color values are all zero). However, because the ADD blend
486 * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
487 * to be created. This makes all source pixels opaque and the colors get copied correctly.
488 */
489 SDL_Surface *src_rotated_rgb = SDL_CreateSurfaceFrom(src_rotated->w, src_rotated->h, src_rotated->format, src_rotated->pixels, src_rotated->pitch);
490 if (!src_rotated_rgb) {
491 result = false;
492 } else {
493 SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
494 // Renderer scaling, if needed
495 result = Blit_to_Screen(src_rotated_rgb, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
496 SDL_DestroySurface(src_rotated_rgb);
497 }
498 }
499 }
500 SDL_DestroySurface(mask_rotated);
501 }
502 if (src_rotated) {
503 SDL_DestroySurface(src_rotated);
504 }
505 }
506 }
507
508 if (SDL_MUSTLOCK(src)) {
509 SDL_UnlockSurface(src);
510 }
511 if (mask) {
512 SDL_DestroySurface(mask);
513 }
514 if (src_clone) {
515 SDL_DestroySurface(src_clone);
516 }
517 return result;
518}
519
520typedef struct GeometryFillData
521{
522 SDL_Point dst;
523 SDL_Color color;
524} GeometryFillData;
525
526typedef struct GeometryCopyData
527{
528 SDL_Point src;
529 SDL_Point dst;
530 SDL_Color color;
531} GeometryCopyData;
532
533static bool SW_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
534 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
535 int num_vertices, const void *indices, int num_indices, int size_indices,
536 float scale_x, float scale_y)
537{
538 int i;
539 int count = indices ? num_indices : num_vertices;
540 void *verts;
541 size_t sz = texture ? sizeof(GeometryCopyData) : sizeof(GeometryFillData);
542 const float color_scale = cmd->data.draw.color_scale;
543
544 verts = SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first);
545 if (!verts) {
546 return false;
547 }
548
549 cmd->data.draw.count = count;
550 size_indices = indices ? size_indices : 0;
551
552 if (texture) {
553 GeometryCopyData *ptr = (GeometryCopyData *)verts;
554 for (i = 0; i < count; i++) {
555 int j;
556 float *xy_;
557 SDL_FColor col_;
558 float *uv_;
559 if (size_indices == 4) {
560 j = ((const Uint32 *)indices)[i];
561 } else if (size_indices == 2) {
562 j = ((const Uint16 *)indices)[i];
563 } else if (size_indices == 1) {
564 j = ((const Uint8 *)indices)[i];
565 } else {
566 j = i;
567 }
568
569 xy_ = (float *)((char *)xy + j * xy_stride);
570 col_ = *(SDL_FColor *)((char *)color + j * color_stride);
571
572 uv_ = (float *)((char *)uv + j * uv_stride);
573
574 ptr->src.x = (int)(uv_[0] * texture->w);
575 ptr->src.y = (int)(uv_[1] * texture->h);
576
577 ptr->dst.x = (int)(xy_[0] * scale_x);
578 ptr->dst.y = (int)(xy_[1] * scale_y);
579 trianglepoint_2_fixedpoint(&ptr->dst);
580
581 ptr->color.r = (Uint8)SDL_roundf(SDL_clamp(col_.r * color_scale, 0.0f, 1.0f) * 255.0f);
582 ptr->color.g = (Uint8)SDL_roundf(SDL_clamp(col_.g * color_scale, 0.0f, 1.0f) * 255.0f);
583 ptr->color.b = (Uint8)SDL_roundf(SDL_clamp(col_.b * color_scale, 0.0f, 1.0f) * 255.0f);
584 ptr->color.a = (Uint8)SDL_roundf(SDL_clamp(col_.a, 0.0f, 1.0f) * 255.0f);
585
586 ptr++;
587 }
588 } else {
589 GeometryFillData *ptr = (GeometryFillData *)verts;
590
591 for (i = 0; i < count; i++) {
592 int j;
593 float *xy_;
594 SDL_FColor col_;
595 if (size_indices == 4) {
596 j = ((const Uint32 *)indices)[i];
597 } else if (size_indices == 2) {
598 j = ((const Uint16 *)indices)[i];
599 } else if (size_indices == 1) {
600 j = ((const Uint8 *)indices)[i];
601 } else {
602 j = i;
603 }
604
605 xy_ = (float *)((char *)xy + j * xy_stride);
606 col_ = *(SDL_FColor *)((char *)color + j * color_stride);
607
608 ptr->dst.x = (int)(xy_[0] * scale_x);
609 ptr->dst.y = (int)(xy_[1] * scale_y);
610 trianglepoint_2_fixedpoint(&ptr->dst);
611
612 ptr->color.r = (Uint8)SDL_roundf(SDL_clamp(col_.r * color_scale, 0.0f, 1.0f) * 255.0f);
613 ptr->color.g = (Uint8)SDL_roundf(SDL_clamp(col_.g * color_scale, 0.0f, 1.0f) * 255.0f);
614 ptr->color.b = (Uint8)SDL_roundf(SDL_clamp(col_.b * color_scale, 0.0f, 1.0f) * 255.0f);
615 ptr->color.a = (Uint8)SDL_roundf(SDL_clamp(col_.a, 0.0f, 1.0f) * 255.0f);
616
617 ptr++;
618 }
619 }
620 return true;
621}
622
623static void PrepTextureForCopy(const SDL_RenderCommand *cmd, SW_DrawStateCache *drawstate)
624{
625 const Uint8 r = drawstate->color.r;
626 const Uint8 g = drawstate->color.g;
627 const Uint8 b = drawstate->color.b;
628 const Uint8 a = drawstate->color.a;
629 const SDL_BlendMode blend = cmd->data.draw.blend;
630 SDL_Texture *texture = cmd->data.draw.texture;
631 SDL_Surface *surface = (SDL_Surface *)texture->internal;
632 const bool colormod = ((r & g & b) != 0xFF);
633 const bool alphamod = (a != 0xFF);
634 const bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD) || (blend == SDL_BLENDMODE_MUL));
635
636 if (colormod || alphamod || blending) {
637 SDL_SetSurfaceRLE(surface, 0);
638 }
639
640 // !!! FIXME: we can probably avoid some of these calls.
641 SDL_SetSurfaceColorMod(surface, r, g, b);
642 SDL_SetSurfaceAlphaMod(surface, a);
643 SDL_SetSurfaceBlendMode(surface, blend);
644}
645
646static void SetDrawState(SDL_Surface *surface, SW_DrawStateCache *drawstate)
647{
648 if (drawstate->surface_cliprect_dirty) {
649 const SDL_Rect *viewport = drawstate->viewport;
650 const SDL_Rect *cliprect = drawstate->cliprect;
651 SDL_assert_release(viewport != NULL); // the higher level should have forced a SDL_RENDERCMD_SETVIEWPORT
652
653 if (cliprect && viewport) {
654 SDL_Rect clip_rect;
655 clip_rect.x = cliprect->x + viewport->x;
656 clip_rect.y = cliprect->y + viewport->y;
657 clip_rect.w = cliprect->w;
658 clip_rect.h = cliprect->h;
659 SDL_GetRectIntersection(viewport, &clip_rect, &clip_rect);
660 SDL_SetSurfaceClipRect(surface, &clip_rect);
661 } else {
662 SDL_SetSurfaceClipRect(surface, drawstate->viewport);
663 }
664 drawstate->surface_cliprect_dirty = false;
665 }
666}
667
668static void SW_InvalidateCachedState(SDL_Renderer *renderer)
669{
670 // SW_DrawStateCache only lives during SW_RunCommandQueue, so nothing to do here!
671}
672
673
674static bool SW_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
675{
676 SDL_Surface *surface = SW_ActivateRenderer(renderer);
677 SW_DrawStateCache drawstate;
678
679 if (!SDL_SurfaceValid(surface)) {
680 return false;
681 }
682
683 drawstate.viewport = NULL;
684 drawstate.cliprect = NULL;
685 drawstate.surface_cliprect_dirty = true;
686 drawstate.color.r = 0;
687 drawstate.color.g = 0;
688 drawstate.color.b = 0;
689 drawstate.color.a = 0;
690
691 while (cmd) {
692 switch (cmd->command) {
693 case SDL_RENDERCMD_SETDRAWCOLOR:
694 {
695 drawstate.color.r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
696 drawstate.color.g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
697 drawstate.color.b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
698 drawstate.color.a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f);
699 break;
700 }
701
702 case SDL_RENDERCMD_SETVIEWPORT:
703 {
704 drawstate.viewport = &cmd->data.viewport.rect;
705 drawstate.surface_cliprect_dirty = true;
706 break;
707 }
708
709 case SDL_RENDERCMD_SETCLIPRECT:
710 {
711 drawstate.cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
712 drawstate.surface_cliprect_dirty = true;
713 break;
714 }
715
716 case SDL_RENDERCMD_CLEAR:
717 {
718 const Uint8 r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
719 const Uint8 g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
720 const Uint8 b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
721 const Uint8 a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f);
722 // By definition the clear ignores the clip rect
723 SDL_SetSurfaceClipRect(surface, NULL);
724 SDL_FillSurfaceRect(surface, NULL, SDL_MapSurfaceRGBA(surface, r, g, b, a));
725 drawstate.surface_cliprect_dirty = true;
726 break;
727 }
728
729 case SDL_RENDERCMD_DRAW_POINTS:
730 {
731 const Uint8 r = drawstate.color.r;
732 const Uint8 g = drawstate.color.g;
733 const Uint8 b = drawstate.color.b;
734 const Uint8 a = drawstate.color.a;
735 const int count = (int)cmd->data.draw.count;
736 SDL_Point *verts = (SDL_Point *)(((Uint8 *)vertices) + cmd->data.draw.first);
737 const SDL_BlendMode blend = cmd->data.draw.blend;
738 SetDrawState(surface, &drawstate);
739
740 // Apply viewport
741 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
742 int i;
743 for (i = 0; i < count; i++) {
744 verts[i].x += drawstate.viewport->x;
745 verts[i].y += drawstate.viewport->y;
746 }
747 }
748
749 if (blend == SDL_BLENDMODE_NONE) {
750 SDL_DrawPoints(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a));
751 } else {
752 SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
753 }
754 break;
755 }
756
757 case SDL_RENDERCMD_DRAW_LINES:
758 {
759 const Uint8 r = drawstate.color.r;
760 const Uint8 g = drawstate.color.g;
761 const Uint8 b = drawstate.color.b;
762 const Uint8 a = drawstate.color.a;
763 const int count = (int)cmd->data.draw.count;
764 SDL_Point *verts = (SDL_Point *)(((Uint8 *)vertices) + cmd->data.draw.first);
765 const SDL_BlendMode blend = cmd->data.draw.blend;
766 SetDrawState(surface, &drawstate);
767
768 // Apply viewport
769 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
770 int i;
771 for (i = 0; i < count; i++) {
772 verts[i].x += drawstate.viewport->x;
773 verts[i].y += drawstate.viewport->y;
774 }
775 }
776
777 if (blend == SDL_BLENDMODE_NONE) {
778 SDL_DrawLines(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a));
779 } else {
780 SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
781 }
782 break;
783 }
784
785 case SDL_RENDERCMD_FILL_RECTS:
786 {
787 const Uint8 r = drawstate.color.r;
788 const Uint8 g = drawstate.color.g;
789 const Uint8 b = drawstate.color.b;
790 const Uint8 a = drawstate.color.a;
791 const int count = (int)cmd->data.draw.count;
792 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first);
793 const SDL_BlendMode blend = cmd->data.draw.blend;
794 SetDrawState(surface, &drawstate);
795
796 // Apply viewport
797 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
798 int i;
799 for (i = 0; i < count; i++) {
800 verts[i].x += drawstate.viewport->x;
801 verts[i].y += drawstate.viewport->y;
802 }
803 }
804
805 if (blend == SDL_BLENDMODE_NONE) {
806 SDL_FillSurfaceRects(surface, verts, count, SDL_MapSurfaceRGBA(surface, r, g, b, a));
807 } else {
808 SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
809 }
810 break;
811 }
812
813 case SDL_RENDERCMD_COPY:
814 {
815 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first);
816 const SDL_Rect *srcrect = verts;
817 SDL_Rect *dstrect = verts + 1;
818 SDL_Texture *texture = cmd->data.draw.texture;
819 SDL_Surface *src = (SDL_Surface *)texture->internal;
820
821 SetDrawState(surface, &drawstate);
822
823 PrepTextureForCopy(cmd, &drawstate);
824
825 // Apply viewport
826 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
827 dstrect->x += drawstate.viewport->x;
828 dstrect->y += drawstate.viewport->y;
829 }
830
831 if (srcrect->w == dstrect->w && srcrect->h == dstrect->h) {
832 SDL_BlitSurface(src, srcrect, surface, dstrect);
833 } else {
834 /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
835 * to avoid potentially frequent RLE encoding/decoding.
836 */
837 SDL_SetSurfaceRLE(surface, 0);
838
839 // Prevent to do scaling + clipping on viewport boundaries as it may lose proportion
840 if (dstrect->x < 0 || dstrect->y < 0 || dstrect->x + dstrect->w > surface->w || dstrect->y + dstrect->h > surface->h) {
841 SDL_Surface *tmp = SDL_CreateSurface(dstrect->w, dstrect->h, src->format);
842 // Scale to an intermediate surface, then blit
843 if (tmp) {
844 SDL_Rect r;
845 SDL_BlendMode blendmode;
846 Uint8 alphaMod, rMod, gMod, bMod;
847
848 SDL_GetSurfaceBlendMode(src, &blendmode);
849 SDL_GetSurfaceAlphaMod(src, &alphaMod);
850 SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
851
852 r.x = 0;
853 r.y = 0;
854 r.w = dstrect->w;
855 r.h = dstrect->h;
856
857 SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
858 SDL_SetSurfaceColorMod(src, 255, 255, 255);
859 SDL_SetSurfaceAlphaMod(src, 255);
860
861 SDL_BlitSurfaceScaled(src, srcrect, tmp, &r, texture->scaleMode);
862
863 SDL_SetSurfaceColorMod(tmp, rMod, gMod, bMod);
864 SDL_SetSurfaceAlphaMod(tmp, alphaMod);
865 SDL_SetSurfaceBlendMode(tmp, blendmode);
866
867 SDL_BlitSurface(tmp, NULL, surface, dstrect);
868 SDL_DestroySurface(tmp);
869 // No need to set back r/g/b/a/blendmode to 'src' since it's done in PrepTextureForCopy()
870 }
871 } else {
872 SDL_BlitSurfaceScaled(src, srcrect, surface, dstrect, texture->scaleMode);
873 }
874 }
875 break;
876 }
877
878 case SDL_RENDERCMD_COPY_EX:
879 {
880 CopyExData *copydata = (CopyExData *)(((Uint8 *)vertices) + cmd->data.draw.first);
881 SetDrawState(surface, &drawstate);
882 PrepTextureForCopy(cmd, &drawstate);
883
884 // Apply viewport
885 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
886 copydata->dstrect.x += drawstate.viewport->x;
887 copydata->dstrect.y += drawstate.viewport->y;
888 }
889
890 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
891 &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip,
892 copydata->scale_x, copydata->scale_y);
893 break;
894 }
895
896 case SDL_RENDERCMD_GEOMETRY:
897 {
898 int i;
899 SDL_Rect *verts = (SDL_Rect *)(((Uint8 *)vertices) + cmd->data.draw.first);
900 const int count = (int)cmd->data.draw.count;
901 SDL_Texture *texture = cmd->data.draw.texture;
902 const SDL_BlendMode blend = cmd->data.draw.blend;
903
904 SetDrawState(surface, &drawstate);
905
906 if (texture) {
907 SDL_Surface *src = (SDL_Surface *)texture->internal;
908
909 GeometryCopyData *ptr = (GeometryCopyData *)verts;
910
911 PrepTextureForCopy(cmd, &drawstate);
912
913 // Apply viewport
914 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
915 SDL_Point vp;
916 vp.x = drawstate.viewport->x;
917 vp.y = drawstate.viewport->y;
918 trianglepoint_2_fixedpoint(&vp);
919 for (i = 0; i < count; i++) {
920 ptr[i].dst.x += vp.x;
921 ptr[i].dst.y += vp.y;
922 }
923 }
924
925 for (i = 0; i < count; i += 3, ptr += 3) {
926 SDL_SW_BlitTriangle(
927 src,
928 &(ptr[0].src), &(ptr[1].src), &(ptr[2].src),
929 surface,
930 &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst),
931 ptr[0].color, ptr[1].color, ptr[2].color,
932 cmd->data.draw.texture_address_mode);
933 }
934 } else {
935 GeometryFillData *ptr = (GeometryFillData *)verts;
936
937 // Apply viewport
938 if (drawstate.viewport && (drawstate.viewport->x || drawstate.viewport->y)) {
939 SDL_Point vp;
940 vp.x = drawstate.viewport->x;
941 vp.y = drawstate.viewport->y;
942 trianglepoint_2_fixedpoint(&vp);
943 for (i = 0; i < count; i++) {
944 ptr[i].dst.x += vp.x;
945 ptr[i].dst.y += vp.y;
946 }
947 }
948
949 for (i = 0; i < count; i += 3, ptr += 3) {
950 SDL_SW_FillTriangle(surface, &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst), blend, ptr[0].color, ptr[1].color, ptr[2].color);
951 }
952 }
953 break;
954 }
955
956 case SDL_RENDERCMD_NO_OP:
957 break;
958 }
959
960 cmd = cmd->next;
961 }
962
963 return true;
964}
965
966static SDL_Surface *SW_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
967{
968 SDL_Surface *surface = SW_ActivateRenderer(renderer);
969 void *pixels;
970
971 if (!SDL_SurfaceValid(surface)) {
972 return NULL;
973 }
974
975 /* NOTE: The rect is already adjusted according to the viewport by
976 * SDL_RenderReadPixels.
977 */
978
979 if (rect->x < 0 || rect->x + rect->w > surface->w ||
980 rect->y < 0 || rect->y + rect->h > surface->h) {
981 SDL_SetError("Tried to read outside of surface bounds");
982 return NULL;
983 }
984
985 pixels = (void *)((Uint8 *)surface->pixels +
986 rect->y * surface->pitch +
987 rect->x * surface->fmt->bytes_per_pixel);
988
989 return SDL_DuplicatePixels(rect->w, rect->h, surface->format, SDL_COLORSPACE_SRGB, pixels, surface->pitch);
990}
991
992static bool SW_RenderPresent(SDL_Renderer *renderer)
993{
994 SDL_Window *window = renderer->window;
995
996 if (!window) {
997 return false;
998 }
999 return SDL_UpdateWindowSurface(window);
1000}
1001
1002static void SW_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
1003{
1004 SDL_Surface *surface = (SDL_Surface *)texture->internal;
1005
1006 SDL_DestroySurface(surface);
1007}
1008
1009static void SW_DestroyRenderer(SDL_Renderer *renderer)
1010{
1011 SDL_Window *window = renderer->window;
1012 SW_RenderData *data = (SW_RenderData *)renderer->internal;
1013
1014 if (window) {
1015 SDL_DestroyWindowSurface(window);
1016 }
1017 SDL_free(data);
1018}
1019
1020static void SW_SelectBestFormats(SDL_Renderer *renderer, SDL_PixelFormat format)
1021{
1022 // Prefer the format used by the framebuffer by default.
1023 SDL_AddSupportedTextureFormat(renderer, format);
1024
1025 switch (format) {
1026 case SDL_PIXELFORMAT_XRGB4444:
1027 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB4444);
1028 break;
1029 case SDL_PIXELFORMAT_XBGR4444:
1030 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR4444);
1031 break;
1032 case SDL_PIXELFORMAT_ARGB4444:
1033 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB4444);
1034 break;
1035 case SDL_PIXELFORMAT_ABGR4444:
1036 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR4444);
1037 break;
1038
1039 case SDL_PIXELFORMAT_XRGB1555:
1040 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB1555);
1041 break;
1042 case SDL_PIXELFORMAT_XBGR1555:
1043 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR1555);
1044 break;
1045 case SDL_PIXELFORMAT_ARGB1555:
1046 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB1555);
1047 break;
1048 case SDL_PIXELFORMAT_ABGR1555:
1049 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR1555);
1050 break;
1051
1052 case SDL_PIXELFORMAT_XRGB8888:
1053 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
1054 break;
1055 case SDL_PIXELFORMAT_RGBX8888:
1056 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA8888);
1057 break;
1058 case SDL_PIXELFORMAT_XBGR8888:
1059 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
1060 break;
1061 case SDL_PIXELFORMAT_BGRX8888:
1062 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA8888);
1063 break;
1064 case SDL_PIXELFORMAT_ARGB8888:
1065 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888);
1066 break;
1067 case SDL_PIXELFORMAT_RGBA8888:
1068 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX8888);
1069 break;
1070 case SDL_PIXELFORMAT_ABGR8888:
1071 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR8888);
1072 break;
1073 case SDL_PIXELFORMAT_BGRA8888:
1074 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX8888);
1075 break;
1076 default:
1077 break;
1078 }
1079
1080 /* Ensure that we always have a SDL_PACKEDLAYOUT_8888 format. Having a matching component order increases the
1081 * chances of getting a fast path for blitting.
1082 */
1083 if (SDL_ISPIXELFORMAT_PACKED(format)) {
1084 if (SDL_PIXELLAYOUT(format) != SDL_PACKEDLAYOUT_8888) {
1085 switch (SDL_PIXELORDER(format)) {
1086 case SDL_PACKEDORDER_BGRX:
1087 case SDL_PACKEDORDER_BGRA:
1088 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX8888);
1089 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA8888);
1090 break;
1091 case SDL_PACKEDORDER_RGBX:
1092 case SDL_PACKEDORDER_RGBA:
1093 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX8888);
1094 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA8888);
1095 break;
1096 case SDL_PACKEDORDER_XBGR:
1097 case SDL_PACKEDORDER_ABGR:
1098 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XBGR8888);
1099 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
1100 break;
1101 case SDL_PACKEDORDER_XRGB:
1102 case SDL_PACKEDORDER_ARGB:
1103 default:
1104 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888);
1105 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
1106 break;
1107 }
1108 }
1109 } else {
1110 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888);
1111 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
1112 }
1113}
1114
1115bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, SDL_PropertiesID create_props)
1116{
1117 SW_RenderData *data;
1118
1119 if (!SDL_SurfaceValid(surface)) {
1120 return SDL_InvalidParamError("surface");
1121 }
1122
1123 renderer->software = true;
1124
1125 data = (SW_RenderData *)SDL_calloc(1, sizeof(*data));
1126 if (!data) {
1127 return false;
1128 }
1129 data->surface = surface;
1130 data->window = surface;
1131
1132 renderer->WindowEvent = SW_WindowEvent;
1133 renderer->GetOutputSize = SW_GetOutputSize;
1134 renderer->CreateTexture = SW_CreateTexture;
1135 renderer->UpdateTexture = SW_UpdateTexture;
1136 renderer->LockTexture = SW_LockTexture;
1137 renderer->UnlockTexture = SW_UnlockTexture;
1138 renderer->SetTextureScaleMode = SW_SetTextureScaleMode;
1139 renderer->SetRenderTarget = SW_SetRenderTarget;
1140 renderer->QueueSetViewport = SW_QueueNoOp;
1141 renderer->QueueSetDrawColor = SW_QueueNoOp;
1142 renderer->QueueDrawPoints = SW_QueueDrawPoints;
1143 renderer->QueueDrawLines = SW_QueueDrawPoints; // lines and points queue vertices the same way.
1144 renderer->QueueFillRects = SW_QueueFillRects;
1145 renderer->QueueCopy = SW_QueueCopy;
1146 renderer->QueueCopyEx = SW_QueueCopyEx;
1147 renderer->QueueGeometry = SW_QueueGeometry;
1148 renderer->InvalidateCachedState = SW_InvalidateCachedState;
1149 renderer->RunCommandQueue = SW_RunCommandQueue;
1150 renderer->RenderReadPixels = SW_RenderReadPixels;
1151 renderer->RenderPresent = SW_RenderPresent;
1152 renderer->DestroyTexture = SW_DestroyTexture;
1153 renderer->DestroyRenderer = SW_DestroyRenderer;
1154 renderer->internal = data;
1155 SW_InvalidateCachedState(renderer);
1156
1157 renderer->name = SW_RenderDriver.name;
1158
1159 SW_SelectBestFormats(renderer, surface->format);
1160
1161 SDL_SetupRendererColorspace(renderer, create_props);
1162
1163 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
1164 return SDL_SetError("Unsupported output colorspace");
1165 }
1166
1167 return true;
1168}
1169
1170static bool SW_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
1171{
1172 // Set the vsync hint based on our flags, if it's not already set
1173 const char *hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
1174 const bool no_hint_set = (!hint || !*hint);
1175
1176 if (no_hint_set) {
1177 if (SDL_GetBooleanProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0)) {
1178 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
1179 } else {
1180 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "0");
1181 }
1182 }
1183
1184 SDL_Surface *surface = SDL_GetWindowSurface(window);
1185
1186 // Reset the vsync hint if we set it above
1187 if (no_hint_set) {
1188 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "");
1189 }
1190
1191 if (!SDL_SurfaceValid(surface)) {
1192 return false;
1193 }
1194
1195 return SW_CreateRendererForSurface(renderer, surface, create_props);
1196}
1197
1198SDL_RenderDriver SW_RenderDriver = {
1199 SW_CreateRenderer, SDL_SOFTWARE_RENDERER
1200};
1201
1202#endif // SDL_VIDEO_RENDER_SW