1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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_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
39 static SDL_Renderer *SW_CreateRenderer(SDL_Window * window, Uint32 flags);
40 static void SW_WindowEvent(SDL_Renderer * renderer,
41 const SDL_WindowEvent *event);
42 static int SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
43 static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
44 static int SW_SetTextureColorMod(SDL_Renderer * renderer,
45 SDL_Texture * texture);
46 static int SW_SetTextureAlphaMod(SDL_Renderer * renderer,
47 SDL_Texture * texture);
48 static int SW_SetTextureBlendMode(SDL_Renderer * renderer,
49 SDL_Texture * texture);
50 static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
51 const SDL_Rect * rect, const void *pixels,
52 int pitch);
53 static int SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
54 const SDL_Rect * rect, void **pixels, int *pitch);
55 static void SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
56 static int SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
57 static int SW_UpdateViewport(SDL_Renderer * renderer);
58 static int SW_UpdateClipRect(SDL_Renderer * renderer);
59 static int SW_RenderClear(SDL_Renderer * renderer);
60 static int SW_RenderDrawPoints(SDL_Renderer * renderer,
61 const SDL_FPoint * points, int count);
62 static int SW_RenderDrawLines(SDL_Renderer * renderer,
63 const SDL_FPoint * points, int count);
64 static int SW_RenderFillRects(SDL_Renderer * renderer,
65 const SDL_FRect * rects, int count);
66 static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
67 const SDL_Rect * srcrect, const SDL_FRect * dstrect);
68 static int SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
69 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
70 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip);
71 static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
72 Uint32 format, void * pixels, int pitch);
73 static void SW_RenderPresent(SDL_Renderer * renderer);
74 static void SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
75 static void SW_DestroyRenderer(SDL_Renderer * renderer);
76
77
78 SDL_RenderDriver SW_RenderDriver = {
79 SW_CreateRenderer,
80 {
81 "software",
82 SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE,
83 8,
84 {
85 SDL_PIXELFORMAT_ARGB8888,
86 SDL_PIXELFORMAT_ABGR8888,
87 SDL_PIXELFORMAT_RGBA8888,
88 SDL_PIXELFORMAT_BGRA8888,
89 SDL_PIXELFORMAT_RGB888,
90 SDL_PIXELFORMAT_BGR888,
91 SDL_PIXELFORMAT_RGB565,
92 SDL_PIXELFORMAT_RGB555
93 },
94 0,
95 0}
96 };
97
98 typedef struct
99 {
100 SDL_Surface *surface;
101 SDL_Surface *window;
102 } SW_RenderData;
103
104
105 static SDL_Surface *
SW_ActivateRenderer(SDL_Renderer * renderer)106 SW_ActivateRenderer(SDL_Renderer * renderer)
107 {
108 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
109
110 if (!data->surface) {
111 data->surface = data->window;
112 }
113 if (!data->surface) {
114 SDL_Surface *surface = SDL_GetWindowSurface(renderer->window);
115 if (surface) {
116 data->surface = data->window = surface;
117
118 SW_UpdateViewport(renderer);
119 SW_UpdateClipRect(renderer);
120 }
121 }
122 return data->surface;
123 }
124
125 SDL_Renderer *
SW_CreateRendererForSurface(SDL_Surface * surface)126 SW_CreateRendererForSurface(SDL_Surface * surface)
127 {
128 SDL_Renderer *renderer;
129 SW_RenderData *data;
130
131 if (!surface) {
132 SDL_SetError("Can't create renderer for NULL surface");
133 return NULL;
134 }
135
136 renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
137 if (!renderer) {
138 SDL_OutOfMemory();
139 return NULL;
140 }
141
142 data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
143 if (!data) {
144 SW_DestroyRenderer(renderer);
145 SDL_OutOfMemory();
146 return NULL;
147 }
148 data->surface = surface;
149 data->window = surface;
150
151 renderer->WindowEvent = SW_WindowEvent;
152 renderer->GetOutputSize = SW_GetOutputSize;
153 renderer->CreateTexture = SW_CreateTexture;
154 renderer->SetTextureColorMod = SW_SetTextureColorMod;
155 renderer->SetTextureAlphaMod = SW_SetTextureAlphaMod;
156 renderer->SetTextureBlendMode = SW_SetTextureBlendMode;
157 renderer->UpdateTexture = SW_UpdateTexture;
158 renderer->LockTexture = SW_LockTexture;
159 renderer->UnlockTexture = SW_UnlockTexture;
160 renderer->SetRenderTarget = SW_SetRenderTarget;
161 renderer->UpdateViewport = SW_UpdateViewport;
162 renderer->UpdateClipRect = SW_UpdateClipRect;
163 renderer->RenderClear = SW_RenderClear;
164 renderer->RenderDrawPoints = SW_RenderDrawPoints;
165 renderer->RenderDrawLines = SW_RenderDrawLines;
166 renderer->RenderFillRects = SW_RenderFillRects;
167 renderer->RenderCopy = SW_RenderCopy;
168 renderer->RenderCopyEx = SW_RenderCopyEx;
169 renderer->RenderReadPixels = SW_RenderReadPixels;
170 renderer->RenderPresent = SW_RenderPresent;
171 renderer->DestroyTexture = SW_DestroyTexture;
172 renderer->DestroyRenderer = SW_DestroyRenderer;
173 renderer->info = SW_RenderDriver.info;
174 renderer->driverdata = data;
175
176 SW_ActivateRenderer(renderer);
177
178 return renderer;
179 }
180
181 SDL_Renderer *
SW_CreateRenderer(SDL_Window * window,Uint32 flags)182 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
183 {
184 SDL_Surface *surface;
185
186 surface = SDL_GetWindowSurface(window);
187 if (!surface) {
188 return NULL;
189 }
190 return SW_CreateRendererForSurface(surface);
191 }
192
193 static void
SW_WindowEvent(SDL_Renderer * renderer,const SDL_WindowEvent * event)194 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
195 {
196 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
197
198 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
199 data->surface = NULL;
200 data->window = NULL;
201 }
202 }
203
204 static int
SW_GetOutputSize(SDL_Renderer * renderer,int * w,int * h)205 SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
206 {
207 SDL_Surface *surface = SW_ActivateRenderer(renderer);
208
209 if (surface) {
210 if (w) {
211 *w = surface->w;
212 }
213 if (h) {
214 *h = surface->h;
215 }
216 return 0;
217 } else {
218 SDL_SetError("Software renderer doesn't have an output surface");
219 return -1;
220 }
221 }
222
223 static int
SW_CreateTexture(SDL_Renderer * renderer,SDL_Texture * texture)224 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
225 {
226 int bpp;
227 Uint32 Rmask, Gmask, Bmask, Amask;
228
229 if (!SDL_PixelFormatEnumToMasks
230 (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
231 return SDL_SetError("Unknown texture format");
232 }
233
234 texture->driverdata =
235 SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
236 Bmask, Amask);
237 SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
238 texture->b);
239 SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
240 SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
241
242 if (texture->access == SDL_TEXTUREACCESS_STATIC) {
243 SDL_SetSurfaceRLE(texture->driverdata, 1);
244 }
245
246 if (!texture->driverdata) {
247 return -1;
248 }
249 return 0;
250 }
251
252 static int
SW_SetTextureColorMod(SDL_Renderer * renderer,SDL_Texture * texture)253 SW_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture)
254 {
255 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
256 /* If the color mod is ever enabled (non-white), permanently disable RLE (which doesn't support
257 * color mod) to avoid potentially frequent RLE encoding/decoding.
258 */
259 if ((texture->r & texture->g & texture->b) != 255) {
260 SDL_SetSurfaceRLE(surface, 0);
261 }
262 return SDL_SetSurfaceColorMod(surface, texture->r, texture->g,
263 texture->b);
264 }
265
266 static int
SW_SetTextureAlphaMod(SDL_Renderer * renderer,SDL_Texture * texture)267 SW_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture)
268 {
269 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
270 /* If the texture ever has multiple alpha values (surface alpha plus alpha channel), permanently
271 * disable RLE (which doesn't support this) to avoid potentially frequent RLE encoding/decoding.
272 */
273 if (texture->a != 255 && surface->format->Amask) {
274 SDL_SetSurfaceRLE(surface, 0);
275 }
276 return SDL_SetSurfaceAlphaMod(surface, texture->a);
277 }
278
279 static int
SW_SetTextureBlendMode(SDL_Renderer * renderer,SDL_Texture * texture)280 SW_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture)
281 {
282 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
283 /* If add or mod blending are ever enabled, permanently disable RLE (which doesn't support
284 * them) to avoid potentially frequent RLE encoding/decoding.
285 */
286 if ((texture->blendMode == SDL_BLENDMODE_ADD || texture->blendMode == SDL_BLENDMODE_MOD)) {
287 SDL_SetSurfaceRLE(surface, 0);
288 }
289 return SDL_SetSurfaceBlendMode(surface, texture->blendMode);
290 }
291
292 static int
SW_UpdateTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const void * pixels,int pitch)293 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
294 const SDL_Rect * rect, const void *pixels, int pitch)
295 {
296 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
297 Uint8 *src, *dst;
298 int row;
299 size_t length;
300
301 if(SDL_MUSTLOCK(surface))
302 SDL_LockSurface(surface);
303 src = (Uint8 *) pixels;
304 dst = (Uint8 *) surface->pixels +
305 rect->y * surface->pitch +
306 rect->x * surface->format->BytesPerPixel;
307 length = rect->w * surface->format->BytesPerPixel;
308 for (row = 0; row < rect->h; ++row) {
309 SDL_memcpy(dst, src, length);
310 src += pitch;
311 dst += surface->pitch;
312 }
313 if(SDL_MUSTLOCK(surface))
314 SDL_UnlockSurface(surface);
315 return 0;
316 }
317
318 static int
SW_LockTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,void ** pixels,int * pitch)319 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
320 const SDL_Rect * rect, void **pixels, int *pitch)
321 {
322 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
323
324 *pixels =
325 (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
326 rect->x * surface->format->BytesPerPixel);
327 *pitch = surface->pitch;
328 return 0;
329 }
330
331 static void
SW_UnlockTexture(SDL_Renderer * renderer,SDL_Texture * texture)332 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
333 {
334 }
335
336 static int
SW_SetRenderTarget(SDL_Renderer * renderer,SDL_Texture * texture)337 SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
338 {
339 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
340
341 if (texture ) {
342 data->surface = (SDL_Surface *) texture->driverdata;
343 } else {
344 data->surface = data->window;
345 }
346 return 0;
347 }
348
349 static int
SW_UpdateViewport(SDL_Renderer * renderer)350 SW_UpdateViewport(SDL_Renderer * renderer)
351 {
352 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
353 SDL_Surface *surface = data->surface;
354
355 if (!surface) {
356 /* We'll update the viewport after we recreate the surface */
357 return 0;
358 }
359
360 SDL_SetClipRect(data->surface, &renderer->viewport);
361 return 0;
362 }
363
364 static int
SW_UpdateClipRect(SDL_Renderer * renderer)365 SW_UpdateClipRect(SDL_Renderer * renderer)
366 {
367 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
368 SDL_Surface *surface = data->surface;
369 if (surface) {
370 if (renderer->clipping_enabled) {
371 SDL_SetClipRect(surface, &renderer->clip_rect);
372 } else {
373 SDL_SetClipRect(surface, NULL);
374 }
375 }
376 return 0;
377 }
378
379 static int
SW_RenderClear(SDL_Renderer * renderer)380 SW_RenderClear(SDL_Renderer * renderer)
381 {
382 SDL_Surface *surface = SW_ActivateRenderer(renderer);
383 Uint32 color;
384 SDL_Rect clip_rect;
385
386 if (!surface) {
387 return -1;
388 }
389
390 color = SDL_MapRGBA(surface->format,
391 renderer->r, renderer->g, renderer->b, renderer->a);
392
393 /* By definition the clear ignores the clip rect */
394 clip_rect = surface->clip_rect;
395 SDL_SetClipRect(surface, NULL);
396 SDL_FillRect(surface, NULL, color);
397 SDL_SetClipRect(surface, &clip_rect);
398 return 0;
399 }
400
401 static int
SW_RenderDrawPoints(SDL_Renderer * renderer,const SDL_FPoint * points,int count)402 SW_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
403 int count)
404 {
405 SDL_Surface *surface = SW_ActivateRenderer(renderer);
406 SDL_Point *final_points;
407 int i, status;
408
409 if (!surface) {
410 return -1;
411 }
412
413 final_points = SDL_stack_alloc(SDL_Point, count);
414 if (!final_points) {
415 return SDL_OutOfMemory();
416 }
417 if (renderer->viewport.x || renderer->viewport.y) {
418 int x = renderer->viewport.x;
419 int y = renderer->viewport.y;
420
421 for (i = 0; i < count; ++i) {
422 final_points[i].x = (int)(x + points[i].x);
423 final_points[i].y = (int)(y + points[i].y);
424 }
425 } else {
426 for (i = 0; i < count; ++i) {
427 final_points[i].x = (int)points[i].x;
428 final_points[i].y = (int)points[i].y;
429 }
430 }
431
432 /* Draw the points! */
433 if (renderer->blendMode == SDL_BLENDMODE_NONE) {
434 Uint32 color = SDL_MapRGBA(surface->format,
435 renderer->r, renderer->g, renderer->b,
436 renderer->a);
437
438 status = SDL_DrawPoints(surface, final_points, count, color);
439 } else {
440 status = SDL_BlendPoints(surface, final_points, count,
441 renderer->blendMode,
442 renderer->r, renderer->g, renderer->b,
443 renderer->a);
444 }
445 SDL_stack_free(final_points);
446
447 return status;
448 }
449
450 static int
SW_RenderDrawLines(SDL_Renderer * renderer,const SDL_FPoint * points,int count)451 SW_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
452 int count)
453 {
454 SDL_Surface *surface = SW_ActivateRenderer(renderer);
455 SDL_Point *final_points;
456 int i, status;
457
458 if (!surface) {
459 return -1;
460 }
461
462 final_points = SDL_stack_alloc(SDL_Point, count);
463 if (!final_points) {
464 return SDL_OutOfMemory();
465 }
466 if (renderer->viewport.x || renderer->viewport.y) {
467 int x = renderer->viewport.x;
468 int y = renderer->viewport.y;
469
470 for (i = 0; i < count; ++i) {
471 final_points[i].x = (int)(x + points[i].x);
472 final_points[i].y = (int)(y + points[i].y);
473 }
474 } else {
475 for (i = 0; i < count; ++i) {
476 final_points[i].x = (int)points[i].x;
477 final_points[i].y = (int)points[i].y;
478 }
479 }
480
481 /* Draw the lines! */
482 if (renderer->blendMode == SDL_BLENDMODE_NONE) {
483 Uint32 color = SDL_MapRGBA(surface->format,
484 renderer->r, renderer->g, renderer->b,
485 renderer->a);
486
487 status = SDL_DrawLines(surface, final_points, count, color);
488 } else {
489 status = SDL_BlendLines(surface, final_points, count,
490 renderer->blendMode,
491 renderer->r, renderer->g, renderer->b,
492 renderer->a);
493 }
494 SDL_stack_free(final_points);
495
496 return status;
497 }
498
499 static int
SW_RenderFillRects(SDL_Renderer * renderer,const SDL_FRect * rects,int count)500 SW_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
501 {
502 SDL_Surface *surface = SW_ActivateRenderer(renderer);
503 SDL_Rect *final_rects;
504 int i, status;
505
506 if (!surface) {
507 return -1;
508 }
509
510 final_rects = SDL_stack_alloc(SDL_Rect, count);
511 if (!final_rects) {
512 return SDL_OutOfMemory();
513 }
514 if (renderer->viewport.x || renderer->viewport.y) {
515 int x = renderer->viewport.x;
516 int y = renderer->viewport.y;
517
518 for (i = 0; i < count; ++i) {
519 final_rects[i].x = (int)(x + rects[i].x);
520 final_rects[i].y = (int)(y + rects[i].y);
521 final_rects[i].w = SDL_max((int)rects[i].w, 1);
522 final_rects[i].h = SDL_max((int)rects[i].h, 1);
523 }
524 } else {
525 for (i = 0; i < count; ++i) {
526 final_rects[i].x = (int)rects[i].x;
527 final_rects[i].y = (int)rects[i].y;
528 final_rects[i].w = SDL_max((int)rects[i].w, 1);
529 final_rects[i].h = SDL_max((int)rects[i].h, 1);
530 }
531 }
532
533 if (renderer->blendMode == SDL_BLENDMODE_NONE) {
534 Uint32 color = SDL_MapRGBA(surface->format,
535 renderer->r, renderer->g, renderer->b,
536 renderer->a);
537 status = SDL_FillRects(surface, final_rects, count, color);
538 } else {
539 status = SDL_BlendFillRects(surface, final_rects, count,
540 renderer->blendMode,
541 renderer->r, renderer->g, renderer->b,
542 renderer->a);
543 }
544 SDL_stack_free(final_rects);
545
546 return status;
547 }
548
549 static int
SW_RenderCopy(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect)550 SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
551 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
552 {
553 SDL_Surface *surface = SW_ActivateRenderer(renderer);
554 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
555 SDL_Rect final_rect;
556
557 if (!surface) {
558 return -1;
559 }
560
561 if (renderer->viewport.x || renderer->viewport.y) {
562 final_rect.x = (int)(renderer->viewport.x + dstrect->x);
563 final_rect.y = (int)(renderer->viewport.y + dstrect->y);
564 } else {
565 final_rect.x = (int)dstrect->x;
566 final_rect.y = (int)dstrect->y;
567 }
568 final_rect.w = (int)dstrect->w;
569 final_rect.h = (int)dstrect->h;
570
571 if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) {
572 return SDL_BlitSurface(src, srcrect, surface, &final_rect);
573 } else {
574 /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
575 * to avoid potentially frequent RLE encoding/decoding.
576 */
577 SDL_SetSurfaceRLE(surface, 0);
578 return SDL_BlitScaled(src, srcrect, surface, &final_rect);
579 }
580 }
581
582 static int
GetScaleQuality(void)583 GetScaleQuality(void)
584 {
585 const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
586
587 if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
588 return 0;
589 } else {
590 return 1;
591 }
592 }
593
594 static int
SW_RenderCopyEx(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)595 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
596 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
597 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
598 {
599 SDL_Surface *surface = SW_ActivateRenderer(renderer);
600 SDL_Surface *src = (SDL_Surface *) texture->driverdata;
601 SDL_Rect final_rect, tmp_rect;
602 SDL_Surface *surface_rotated, *surface_scaled;
603 int retval, dstwidth, dstheight, abscenterx, abscentery;
604 double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
605
606 if (!surface) {
607 return -1;
608 }
609
610 if (renderer->viewport.x || renderer->viewport.y) {
611 final_rect.x = (int)(renderer->viewport.x + dstrect->x);
612 final_rect.y = (int)(renderer->viewport.y + dstrect->y);
613 } else {
614 final_rect.x = (int)dstrect->x;
615 final_rect.y = (int)dstrect->y;
616 }
617 final_rect.w = (int)dstrect->w;
618 final_rect.h = (int)dstrect->h;
619
620 /* SDLgfx_rotateSurface doesn't accept a source rectangle, so crop and scale if we need to */
621 tmp_rect = final_rect;
622 tmp_rect.x = 0;
623 tmp_rect.y = 0;
624 if (srcrect->w == final_rect.w && srcrect->h == final_rect.h && srcrect->x == 0 && srcrect->y == 0) {
625 surface_scaled = src; /* but if we don't need to, just use the original */
626 retval = 0;
627 } else {
628 SDL_Surface *blit_src = src;
629 Uint32 colorkey;
630 SDL_BlendMode blendMode;
631 Uint8 alphaMod, r, g, b;
632 SDL_bool cloneSource = SDL_FALSE;
633
634 surface_scaled = SDL_CreateRGBSurface(SDL_SWSURFACE, final_rect.w, final_rect.h, src->format->BitsPerPixel,
635 src->format->Rmask, src->format->Gmask,
636 src->format->Bmask, src->format->Amask );
637 if (!surface_scaled) {
638 return -1;
639 }
640
641 /* copy the color key, alpha mod, blend mode, and color mod so the scaled surface behaves like the source */
642 if (SDL_GetColorKey(src, &colorkey) == 0) {
643 SDL_SetColorKey(surface_scaled, SDL_TRUE, colorkey);
644 cloneSource = SDL_TRUE;
645 }
646 SDL_GetSurfaceAlphaMod(src, &alphaMod); /* these will be copied to surface_scaled below if necessary */
647 SDL_GetSurfaceBlendMode(src, &blendMode);
648 SDL_GetSurfaceColorMod(src, &r, &g, &b);
649
650 /* now we need to blit the src into surface_scaled. since we want to copy the colors from the source to
651 * surface_scaled rather than blend them, etc. we'll need to disable the blend mode, alpha mod, etc.
652 * but we don't want to modify src (in case it's being used on other threads), so we'll need to clone it
653 * before changing the blend options
654 */
655 cloneSource |= blendMode != SDL_BLENDMODE_NONE || (alphaMod & r & g & b) != 255;
656 if (cloneSource) {
657 blit_src = SDL_ConvertSurface(src, src->format, src->flags); /* clone src */
658 if (!blit_src) {
659 SDL_FreeSurface(surface_scaled);
660 return -1;
661 }
662 SDL_SetSurfaceAlphaMod(blit_src, 255); /* disable all blending options in blit_src */
663 SDL_SetSurfaceBlendMode(blit_src, SDL_BLENDMODE_NONE);
664 SDL_SetColorKey(blit_src, 0, 0);
665 SDL_SetSurfaceColorMod(blit_src, 255, 255, 255);
666 SDL_SetSurfaceRLE(blit_src, 0); /* don't RLE encode a surface we'll only use once */
667
668 SDL_SetSurfaceAlphaMod(surface_scaled, alphaMod); /* copy blending options to surface_scaled */
669 SDL_SetSurfaceBlendMode(surface_scaled, blendMode);
670 SDL_SetSurfaceColorMod(surface_scaled, r, g, b);
671 }
672
673 retval = SDL_BlitScaled(blit_src, srcrect, surface_scaled, &tmp_rect);
674 if (blit_src != src) {
675 SDL_FreeSurface(blit_src);
676 }
677 }
678
679 if (!retval) {
680 SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
681 surface_rotated = SDLgfx_rotateSurface(surface_scaled, angle, dstwidth/2, dstheight/2, GetScaleQuality(), flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
682 if(surface_rotated) {
683 /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
684 abscenterx = final_rect.x + (int)center->x;
685 abscentery = final_rect.y + (int)center->y;
686 /* Compensate the angle inversion to match the behaviour of the other backends */
687 sangle = -sangle;
688
689 /* Top Left */
690 px = final_rect.x - abscenterx;
691 py = final_rect.y - abscentery;
692 p1x = px * cangle - py * sangle + abscenterx;
693 p1y = px * sangle + py * cangle + abscentery;
694
695 /* Top Right */
696 px = final_rect.x + final_rect.w - abscenterx;
697 py = final_rect.y - abscentery;
698 p2x = px * cangle - py * sangle + abscenterx;
699 p2y = px * sangle + py * cangle + abscentery;
700
701 /* Bottom Left */
702 px = final_rect.x - abscenterx;
703 py = final_rect.y + final_rect.h - abscentery;
704 p3x = px * cangle - py * sangle + abscenterx;
705 p3y = px * sangle + py * cangle + abscentery;
706
707 /* Bottom Right */
708 px = final_rect.x + final_rect.w - abscenterx;
709 py = final_rect.y + final_rect.h - abscentery;
710 p4x = px * cangle - py * sangle + abscenterx;
711 p4y = px * sangle + py * cangle + abscentery;
712
713 tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
714 tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
715 tmp_rect.w = dstwidth;
716 tmp_rect.h = dstheight;
717
718 retval = SDL_BlitSurface(surface_rotated, NULL, surface, &tmp_rect);
719 SDL_FreeSurface(surface_rotated);
720 }
721 }
722
723 if (surface_scaled != src) {
724 SDL_FreeSurface(surface_scaled);
725 }
726 return retval;
727 }
728
729 static int
SW_RenderReadPixels(SDL_Renderer * renderer,const SDL_Rect * rect,Uint32 format,void * pixels,int pitch)730 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
731 Uint32 format, void * pixels, int pitch)
732 {
733 SDL_Surface *surface = SW_ActivateRenderer(renderer);
734 Uint32 src_format;
735 void *src_pixels;
736 SDL_Rect final_rect;
737
738 if (!surface) {
739 return -1;
740 }
741
742 if (renderer->viewport.x || renderer->viewport.y) {
743 final_rect.x = renderer->viewport.x + rect->x;
744 final_rect.y = renderer->viewport.y + rect->y;
745 final_rect.w = rect->w;
746 final_rect.h = rect->h;
747 rect = &final_rect;
748 }
749
750 if (rect->x < 0 || rect->x+rect->w > surface->w ||
751 rect->y < 0 || rect->y+rect->h > surface->h) {
752 return SDL_SetError("Tried to read outside of surface bounds");
753 }
754
755 src_format = surface->format->format;
756 src_pixels = (void*)((Uint8 *) surface->pixels +
757 rect->y * surface->pitch +
758 rect->x * surface->format->BytesPerPixel);
759
760 return SDL_ConvertPixels(rect->w, rect->h,
761 src_format, src_pixels, surface->pitch,
762 format, pixels, pitch);
763 }
764
765 static void
SW_RenderPresent(SDL_Renderer * renderer)766 SW_RenderPresent(SDL_Renderer * renderer)
767 {
768 SDL_Window *window = renderer->window;
769
770 if (window) {
771 SDL_UpdateWindowSurface(window);
772 }
773 }
774
775 static void
SW_DestroyTexture(SDL_Renderer * renderer,SDL_Texture * texture)776 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
777 {
778 SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
779
780 SDL_FreeSurface(surface);
781 }
782
783 static void
SW_DestroyRenderer(SDL_Renderer * renderer)784 SW_DestroyRenderer(SDL_Renderer * renderer)
785 {
786 SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
787
788 SDL_free(data);
789 SDL_free(renderer);
790 }
791
792 #endif /* !SDL_RENDER_DISABLED */
793
794 /* vi: set ts=4 sw=4 expandtab: */
795