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