GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/sdl2gfx/SDL2_rotozoom.cpp Lines: 0 541 0.0 %
Date: 2021-03-17 Branches: 0 305 0.0 %

Line Branch Exec Source
1
/*
2
3
SDL2_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
4
5
Copyright (C) 2012-2014  Andreas Schiffler
6
7
This software is provided 'as-is', without any express or implied
8
warranty. In no event will the authors be held liable for any damages
9
arising from the use of this software.
10
11
Permission is granted to anyone to use this software for any purpose,
12
including commercial applications, and to alter it and redistribute it
13
freely, subject to the following restrictions:
14
15
1. The origin of this software must not be misrepresented; you must not
16
claim that you wrote the original software. If you use this software
17
in a product, an acknowledgment in the product documentation would be
18
appreciated but is not required.
19
20
2. Altered source versions must be plainly marked as such, and must not be
21
misrepresented as being the original software.
22
23
3. This notice may not be removed or altered from any source
24
distribution.
25
26
Andreas Schiffler -- aschiffler at ferzkopp dot net
27
28
Changed for ManaPlus (C) 2013-2017  ManaPlus developers
29
30
*/
31
32
#ifdef WIN32
33
#include <windows.h>
34
#endif
35
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include "sdl2gfx/SDL2_rotozoom.h"
40
41
#include "debug.h"
42
43
/* ---- Internally used structures */
44
45
/*!
46
\brief A 32 bit RGBA pixel.
47
*/
48
typedef struct tColorRGBA
49
{
50
    Uint8 r;
51
    Uint8 g;
52
    Uint8 b;
53
    Uint8 a;
54
} tColorRGBA;
55
56
/*!
57
\brief A 8bit Y/palette pixel.
58
*/
59
typedef struct tColorY
60
{
61
    Uint8 y;
62
} tColorY;
63
64
/*!
65
\brief Returns maximum of two numbers a and b.
66
*/
67
#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
68
69
/*!
70
\brief Number of guard rows added to destination surfaces.
71
72
This is a simple but effective workaround for observed issues.
73
These rows allocate extra memory and are then hidden from the surface.
74
Rows are added to the end of destination surfaces when they are allocated.
75
This catches any potential overflows which seem to happen with
76
just the right src image dimensions and scale/rotation and can lead
77
to a situation where the program can segfault.
78
*/
79
#define GUARD_ROWS (2)
80
81
/*!
82
\brief Lower limit of absolute zoom factor or rotation degrees.
83
*/
84
#define VALUE_LIMIT    0.001
85
86
/*!
87
\brief Returns colorkey info for a surface
88
*/
89
static Uint32 _colorkey(SDL_Surface *src)
90
{
91
    Uint32 key = 0;
92
    SDL_GetColorKey(src, &key);
93
    return key;
94
}
95
96
97
/*!
98
\brief Internal 32 bit integer-factor averaging Shrinker.
99
100
Shrinks 32 bit RGBA/ABGR 'src' surface to 'dst' surface.
101
Averages color and alpha values values of src pixels to calculate dst pixels.
102
Assumes src and dst surfaces are of 32 bit depth.
103
Assumes dst surface was allocated with the correct dimensions.
104
105
\param src The surface to shrink (input).
106
\param dst The shrunken surface (output).
107
\param factorx The horizontal shrinking ratio.
108
\param factory The vertical shrinking ratio.
109
110
\return 0 for success or -1 for error.
111
*/
112
static int _shrinkSurfaceRGBA(SDL_Surface *src,
113
                              SDL_Surface *dst,
114
                              int factorx,
115
                              int factory)
116
{
117
    int x, y, dx, dy, dgap, ra, ga, ba, aa;
118
    int n_average;
119
    tColorRGBA *sp, *osp, *oosp;
120
    tColorRGBA *dp;
121
122
    /*
123
    * Averaging integer shrink
124
    */
125
126
    /* Precalculate division factor */
127
    n_average = factorx*factory;
128
129
    /*
130
    * Scan destination
131
    */
132
    sp = static_cast<tColorRGBA *>(src->pixels);
133
134
    dp = static_cast<tColorRGBA *>(dst->pixels);
135
    dgap = dst->pitch - dst->w * 4;
136
137
    for (y = 0; y < dst->h; y++)
138
    {
139
        osp=sp;
140
        for (x = 0; x < dst->w; x++)
141
        {
142
            /* Trace out source box and accumulate */
143
            oosp=sp;
144
            ra=ga=ba=aa=0;
145
            for (dy=0; dy < factory; dy++)
146
            {
147
                for (dx=0; dx < factorx; dx++)
148
                {
149
                    ra += sp->r;
150
                    ga += sp->g;
151
                    ba += sp->b;
152
                    aa += sp->a;
153
154
                    sp++;
155
                }
156
                /* src dx loop */
157
                sp = reinterpret_cast<tColorRGBA *>(
158
                    reinterpret_cast<Uint8*>(sp) +
159
                    (src->pitch - 4*factorx)); // next y
160
            }
161
            /* src dy loop */
162
163
            /* next box-x */
164
            sp = reinterpret_cast<tColorRGBA *>(
165
                reinterpret_cast<Uint8*>(oosp) + 4*factorx);
166
167
            /* Store result in destination */
168
            dp->r = ra/n_average;
169
            dp->g = ga/n_average;
170
            dp->b = ba/n_average;
171
            dp->a = aa/n_average;
172
173
            /*
174
            * Advance destination pointer
175
            */
176
            dp++;
177
        }
178
        /* dst x loop */
179
180
        /* next box-y */
181
        sp = reinterpret_cast<tColorRGBA *>(reinterpret_cast<Uint8*>(osp) +
182
            src->pitch*factory);
183
184
        /*
185
        * Advance destination pointers
186
        */
187
        dp = reinterpret_cast<tColorRGBA *>(reinterpret_cast<Uint8*>(dp) +
188
            dgap);
189
    }
190
    /* dst y loop */
191
192
    return (0);
193
}
194
195
/*!
196
\brief Internal 8 bit integer-factor averaging shrinker.
197
198
Shrinks 8bit Y 'src' surface to 'dst' surface.
199
Averages color (brightness) values values of src pixels to calculate dst pixels.
200
Assumes src and dst surfaces are of 8 bit depth.
201
Assumes dst surface was allocated with the correct dimensions.
202
203
\param src The surface to shrink (input).
204
\param dst The shrunken surface (output).
205
\param factorx The horizontal shrinking ratio.
206
\param factory The vertical shrinking ratio.
207
208
\return 0 for success or -1 for error.
209
*/
210
static int _shrinkSurfaceY(SDL_Surface *src,
211
                           SDL_Surface *dst,
212
                           int factorx,
213
                           int factory)
214
{
215
    int x, y, dx, dy, dgap, a;
216
    int n_average;
217
    Uint8 *sp, *osp, *oosp;
218
    Uint8 *dp;
219
220
    /*
221
    * Averaging integer shrink
222
    */
223
224
    /* Precalculate division factor */
225
    n_average = factorx*factory;
226
227
    /*
228
    * Scan destination
229
    */
230
    sp = static_cast<Uint8*>(src->pixels);
231
232
    dp = static_cast<Uint8*>(dst->pixels);
233
    dgap = dst->pitch - dst->w;
234
235
    for (y = 0; y < dst->h; y++)
236
    {
237
        osp=sp;
238
        for (x = 0; x < dst->w; x++)
239
        {
240
            /* Trace out source box and accumulate */
241
            oosp=sp;
242
            a=0;
243
            for (dy=0; dy < factory; dy++)
244
            {
245
                for (dx=0; dx < factorx; dx++)
246
                {
247
                    a += (*sp);
248
                    /* next x */
249
                    sp++;
250
                }
251
                /* end src dx loop */
252
                /* next y */
253
                sp = static_cast<Uint8*>(static_cast<Uint8*>(sp) +
254
                    (src->pitch - factorx));
255
            }
256
            /* end src dy loop */
257
258
            /* next box-x */
259
            sp = static_cast<Uint8*>(static_cast<Uint8*>(oosp) + factorx);
260
261
            /* Store result in destination */
262
            *dp = a/n_average;
263
264
            /*
265
            * Advance destination pointer
266
            */
267
            dp++;
268
        }
269
        /* end dst x loop */
270
271
        /* next box-y */
272
        sp = static_cast<Uint8*>(static_cast<Uint8*>(osp) +
273
            src->pitch*factory);
274
275
        /*
276
        * Advance destination pointers
277
        */
278
        dp = static_cast<Uint8*>(static_cast<Uint8*>(dp) + dgap);
279
    }
280
    /* end dst y loop */
281
282
    return (0);
283
}
284
285
/*!
286
\brief Internal 32 bit Zoomer with optional anti-aliasing by bilinear interpolation.
287
288
Zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface.
289
Assumes src and dst surfaces are of 32 bit depth.
290
Assumes dst surface was allocated with the correct dimensions.
291
292
\param src The surface to zoom (input).
293
\param dst The zoomed surface (output).
294
\param flipx Flag indicating if the image should be horizontally flipped.
295
\param flipy Flag indicating if the image should be vertically flipped.
296
\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
297
298
\return 0 for success or -1 for error.
299
*/
300
static int _zoomSurfaceRGBA(SDL_Surface *src,
301
                            SDL_Surface *dst,
302
                            int flipx,
303
                            int flipy,
304
                            int smooth)
305
{
306
    int x, y, sx, sy, ssx, ssy;
307
    int *sax, *say, *csax, *csay, *salast;
308
    int csx, csy, ex, ey, cx, cy, sstep, sstepx, sstepy;
309
    tColorRGBA *c00, *c01, *c10, *c11;
310
    tColorRGBA *sp, *csp, *dp;
311
    int spixelgap, spixelw, spixelh, dgap, t1, t2;
312
313
    /*
314
    * Allocate memory for row/column increments
315
    */
316
    if ((sax = static_cast<int *>(malloc((dst->w + 1) * sizeof(Uint32)))) == nullptr)
317
    {
318
        return (-1);
319
    }
320
    if ((say = static_cast<int *>(malloc((dst->h + 1) * sizeof(Uint32)))) == nullptr)
321
    {
322
        free(sax);
323
        return (-1);
324
    }
325
326
    /*
327
    * Precalculate row increments
328
    */
329
    spixelw = (src->w - 1);
330
    spixelh = (src->h - 1);
331
    if (smooth)
332
    {
333
        sx = static_cast<int>(65536.0 * static_cast<float>(spixelw) /
334
            static_cast<float>(dst->w - 1));
335
        sy = static_cast<int>(65536.0 * static_cast<float>(spixelh) /
336
            static_cast<float>(dst->h - 1));
337
    }
338
    else
339
    {
340
        sx = static_cast<int>(65536.0 * static_cast<float>(src->w) /
341
            static_cast<float>(dst->w));
342
        sy = static_cast<int>(65536.0 * static_cast<float>(src->h) /
343
            static_cast<float>(dst->h));
344
    }
345
346
    /* Maximum scaled source size */
347
    ssx = (src->w << 16) - 1;
348
    ssy = (src->h << 16) - 1;
349
350
    /* Precalculate horizontal row increments */
351
    csx = 0;
352
    csax = sax;
353
    for (x = 0; x <= dst->w; x++)
354
    {
355
        *csax = csx;
356
        csax++;
357
        csx += sx;
358
359
        /* Guard from overflows */
360
        if (csx > ssx)
361
        {
362
            csx = ssx;
363
        }
364
    }
365
366
    /* Precalculate vertical row increments */
367
    csy = 0;
368
    csay = say;
369
    for (y = 0; y <= dst->h; y++)
370
    {
371
        *csay = csy;
372
        csay++;
373
        csy += sy;
374
375
        /* Guard from overflows */
376
        if (csy > ssy)
377
        {
378
            csy = ssy;
379
        }
380
    }
381
382
    sp = static_cast<tColorRGBA *>(src->pixels);
383
    dp = static_cast<tColorRGBA *>(dst->pixels);
384
    dgap = dst->pitch - dst->w * 4;
385
    spixelgap = src->pitch/4;
386
387
    if (flipx) sp += spixelw;
388
    if (flipy) sp += (spixelgap * spixelh);
389
390
    /*
391
    * Switch between interpolating and non-interpolating code
392
    */
393
    if (smooth)
394
    {
395
        /*
396
        * Interpolating Zoom
397
        */
398
        csay = say;
399
        for (y = 0; y < dst->h; y++)
400
        {
401
            csp = sp;
402
            csax = sax;
403
            for (x = 0; x < dst->w; x++)
404
            {
405
                /*
406
                * Setup color source pointers
407
                */
408
                ex = (*csax & 0xffff);
409
                ey = (*csay & 0xffff);
410
                cx = (*csax >> 16);
411
                cy = (*csay >> 16);
412
                sstepx = cx < spixelw;
413
                sstepy = cy < spixelh;
414
                c00 = sp;
415
                c01 = sp;
416
                c10 = sp;
417
                if (sstepy)
418
                {
419
                    if (flipy)
420
                    {
421
                        c10 -= spixelgap;
422
                    }
423
                    else
424
                    {
425
                        c10 += spixelgap;
426
                    }
427
                }
428
                c11 = c10;
429
                if (sstepx)
430
                {
431
                    if (flipx)
432
                    {
433
                        c01--;
434
                        c11--;
435
                    }
436
                    else
437
                    {
438
                        c01++;
439
                        c11++;
440
                    }
441
                }
442
443
                /*
444
                * Draw and interpolate colors
445
                */
446
                t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
447
                t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
448
                dp->r = (((t2 - t1) * ey) >> 16) + t1;
449
                t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
450
                t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
451
                dp->g = (((t2 - t1) * ey) >> 16) + t1;
452
                t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
453
                t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
454
                dp->b = (((t2 - t1) * ey) >> 16) + t1;
455
                t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
456
                t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
457
                dp->a = (((t2 - t1) * ey) >> 16) + t1;
458
                /*
459
                * Advance source pointer x
460
                */
461
                salast = csax;
462
                csax++;
463
                sstep = (*csax >> 16) - (*salast >> 16);
464
                if (flipx)
465
                {
466
                    sp -= sstep;
467
                }
468
                else
469
                {
470
                    sp += sstep;
471
                }
472
473
                /*
474
                * Advance destination pointer x
475
                */
476
                dp++;
477
            }
478
            /*
479
            * Advance source pointer y
480
            */
481
            salast = csay;
482
            csay++;
483
            sstep = (*csay >> 16) - (*salast >> 16);
484
            sstep *= spixelgap;
485
            if (flipy)
486
            {
487
                sp = csp - sstep;
488
            }
489
            else
490
            {
491
                sp = csp + sstep;
492
            }
493
494
            /*
495
            * Advance destination pointer y
496
            */
497
            dp = reinterpret_cast<tColorRGBA *>(
498
                reinterpret_cast<Uint8*>(dp) + dgap);
499
        }
500
    }
501
    else
502
    {
503
        /*
504
        * Non-Interpolating Zoom
505
        */
506
        csay = say;
507
        for (y = 0; y < dst->h; y++)
508
        {
509
            csp = sp;
510
            csax = sax;
511
            for (x = 0; x < dst->w; x++)
512
            {
513
                /*
514
                * Draw
515
                */
516
                *dp = *sp;
517
518
                /*
519
                * Advance source pointer x
520
                */
521
                salast = csax;
522
                csax++;
523
                sstep = (*csax >> 16) - (*salast >> 16);
524
                if (flipx) sstep = -sstep;
525
                sp += sstep;
526
527
                /*
528
                * Advance destination pointer x
529
                */
530
                dp++;
531
            }
532
            /*
533
            * Advance source pointer y
534
            */
535
            salast = csay;
536
            csay++;
537
            sstep = (*csay >> 16) - (*salast >> 16);
538
            sstep *= spixelgap;
539
            if (flipy) sstep = -sstep;
540
            sp = csp + sstep;
541
542
            /*
543
            * Advance destination pointer y
544
            */
545
            dp = reinterpret_cast<tColorRGBA *>(
546
                reinterpret_cast<Uint8*>(dp) + dgap);
547
        }
548
    }
549
550
    /*
551
    * Remove temp arrays
552
    */
553
    free(sax);
554
    free(say);
555
556
    return (0);
557
}
558
559
/*!
560
561
\brief Internal 8 bit Zoomer without smoothing.
562
563
Zooms 8bit palette/Y 'src' surface to 'dst' surface.
564
Assumes src and dst surfaces are of 8 bit depth.
565
Assumes dst surface was allocated with the correct dimensions.
566
567
\param src The surface to zoom (input).
568
\param dst The zoomed surface (output).
569
\param flipx Flag indicating if the image should be horizontally flipped.
570
\param flipy Flag indicating if the image should be vertically flipped.
571
572
\return 0 for success or -1 for error.
573
*/
574
static int _zoomSurfaceY(SDL_Surface *src,
575
                         SDL_Surface *dst,
576
                         int flipx,
577
                         int flipy)
578
{
579
    int x, y;
580
    Uint32 *sax, *say, *csax, *csay;
581
    int csx, csy;
582
    Uint8 *sp, *dp, *csp;
583
    int dgap;
584
585
    /*
586
    * Allocate memory for row increments
587
    */
588
    if ((sax = static_cast<Uint32 *>(malloc((dst->w + 1) *
589
        sizeof(Uint32)))) == nullptr)
590
    {
591
        return (-1);
592
    }
593
    if ((say = static_cast<Uint32 *>(malloc((dst->h + 1) *
594
        sizeof(Uint32)))) == nullptr)
595
    {
596
        free(sax);
597
        return (-1);
598
    }
599
600
    /*
601
    * Pointer setup
602
    */
603
    sp = csp = static_cast<Uint8*>(src->pixels);
604
    dp = static_cast<Uint8*>(dst->pixels);
605
    dgap = dst->pitch - dst->w;
606
607
    if (flipx) csp += (src->w-1);
608
    if (flipy) csp  = (static_cast<Uint8*>(csp) + src->pitch*(src->h-1));
609
610
    /*
611
    * Precalculate row increments
612
    */
613
    csx = 0;
614
    csax = sax;
615
    for (x = 0; x < dst->w; x++)
616
    {
617
        csx += src->w;
618
        *csax = 0;
619
        while (csx >= dst->w)
620
        {
621
            csx -= dst->w;
622
            (*csax)++;
623
        }
624
        (*csax) = (*csax) * (flipx ? -1 : 1);
625
        csax++;
626
    }
627
    csy = 0;
628
    csay = say;
629
    for (y = 0; y < dst->h; y++)
630
    {
631
        csy += src->h;
632
        *csay = 0;
633
        while (csy >= dst->h)
634
        {
635
            csy -= dst->h;
636
            (*csay)++;
637
        }
638
        (*csay) = (*csay) * (flipy ? -1 : 1);
639
        csay++;
640
    }
641
642
    /*
643
    * Draw
644
    */
645
    csay = say;
646
    for (y = 0; y < dst->h; y++)
647
    {
648
        csax = sax;
649
        sp = csp;
650
        for (x = 0; x < dst->w; x++)
651
        {
652
            /*
653
            * Draw
654
            */
655
            *dp = *sp;
656
            /*
657
            * Advance source pointers
658
            */
659
            sp += (*csax);
660
            csax++;
661
            /*
662
            * Advance destination pointer
663
            */
664
            dp++;
665
        }
666
        /*
667
        * Advance source pointer (for row)
668
        */
669
        csp += ((*csay) * src->pitch);
670
        csay++;
671
672
        /*
673
        * Advance destination pointers
674
        */
675
        dp += dgap;
676
    }
677
678
    /*
679
    * Remove temp arrays
680
    */
681
    free(sax);
682
    free(say);
683
684
    return (0);
685
}
686
687
/*!
688
\brief Internal 32 bit rotozoomer with optional anti-aliasing.
689
690
Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
691
parameters by scanning the destination surface and applying optionally anti-aliasing
692
by bilinear interpolation.
693
Assumes src and dst surfaces are of 32 bit depth.
694
Assumes dst surface was allocated with the correct dimensions.
695
696
\param src Source surface.
697
\param dst Destination surface.
698
\param cx Horizontal center coordinate.
699
\param cy Vertical center coordinate.
700
\param isin Integer version of sine of angle.
701
\param icos Integer version of cosine of angle.
702
\param flipx Flag indicating horizontal mirroring should be applied.
703
\param flipy Flag indicating vertical mirroring should be applied.
704
\param smooth Flag indicating anti-aliasing should be used.
705
*/
706
static void _transformSurfaceRGBA(SDL_Surface *src,
707
                                  SDL_Surface *dst,
708
                                  int cx,
709
                                  int cy,
710
                                  int isin,
711
                                  int icos,
712
                                  int flipx,
713
                                  int flipy,
714
                                  int smooth)
715
{
716
    int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
717
    tColorRGBA c00, c01, c10, c11, cswap;
718
    tColorRGBA *pc, *sp;
719
    int gap;
720
721
    /*
722
    * Variable setup
723
    */
724
    xd = ((src->w - dst->w) << 15);
725
    yd = ((src->h - dst->h) << 15);
726
    ax = (cx << 16) - (icos * cx);
727
    ay = (cy << 16) - (isin * cx);
728
    sw = src->w - 1;
729
    sh = src->h - 1;
730
    pc = static_cast<tColorRGBA*>(dst->pixels);
731
    gap = dst->pitch - dst->w * 4;
732
733
    /*
734
    * Switch between interpolating and non-interpolating code
735
    */
736
    if (smooth)
737
    {
738
        for (y = 0; y < dst->h; y++)
739
        {
740
            dy = cy - y;
741
            sdx = (ax + (isin * dy)) + xd;
742
            sdy = (ay - (icos * dy)) + yd;
743
            for (x = 0; x < dst->w; x++)
744
            {
745
                dx = (sdx >> 16);
746
                dy = (sdy >> 16);
747
                if (flipx) dx = sw - dx;
748
                if (flipy) dy = sh - dy;
749
                if ((dx > -1) && (dy > -1) &&
750
                    (dx < (src->w-1)) && (dy < (src->h-1)))
751
                {
752
                    sp = static_cast<tColorRGBA *>(src->pixels);
753
                    sp += ((src->pitch/4) * dy);
754
                    sp += dx;
755
                    c00 = *sp;
756
                    sp += 1;
757
                    c01 = *sp;
758
                    sp += (src->pitch/4);
759
                    c11 = *sp;
760
                    sp -= 1;
761
                    c10 = *sp;
762
                    if (flipx)
763
                    {
764
                        cswap = c00; c00=c01; c01=cswap;
765
                        cswap = c10; c10=c11; c11=cswap;
766
                    }
767
                    if (flipy)
768
                    {
769
                        cswap = c00; c00=c10; c10=cswap;
770
                        cswap = c01; c01=c11; c11=cswap;
771
                    }
772
                    /*
773
                    * Interpolate colors
774
                    */
775
                    ex = (sdx & 0xffff);
776
                    ey = (sdy & 0xffff);
777
                    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
778
                    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
779
                    pc->r = (((t2 - t1) * ey) >> 16) + t1;
780
                    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
781
                    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
782
                    pc->g = (((t2 - t1) * ey) >> 16) + t1;
783
                    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
784
                    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
785
                    pc->b = (((t2 - t1) * ey) >> 16) + t1;
786
                    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
787
                    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
788
                    pc->a = (((t2 - t1) * ey) >> 16) + t1;
789
                }
790
                sdx += icos;
791
                sdy += isin;
792
                pc++;
793
            }
794
            pc = reinterpret_cast<tColorRGBA *>(
795
                reinterpret_cast<Uint8*>(pc) + gap);
796
        }
797
    }
798
    else
799
    {
800
        for (y = 0; y < dst->h; y++)
801
        {
802
            dy = cy - y;
803
            sdx = (ax + (isin * dy)) + xd;
804
            sdy = (ay - (icos * dy)) + yd;
805
            for (x = 0; x < dst->w; x++)
806
            {
807
                dx = static_cast<short>(sdx >> 16);
808
                dy = static_cast<short>(sdy >> 16);
809
                if (flipx) dx = (src->w-1)-dx;
810
                if (flipy) dy = (src->h-1)-dy;
811
                if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h))
812
                {
813
                    sp = reinterpret_cast<tColorRGBA *>(
814
                        reinterpret_cast<Uint8*>(src->pixels) +
815
                        src->pitch * dy);
816
                    sp += dx;
817
                    *pc = *sp;
818
                }
819
                sdx += icos;
820
                sdy += isin;
821
                pc++;
822
            }
823
            pc = reinterpret_cast<tColorRGBA *>(
824
                reinterpret_cast<Uint8*>(pc) + gap);
825
        }
826
    }
827
}
828
829
/*!
830
831
\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
832
833
Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
834
parameters by scanning the destination surface.
835
Assumes src and dst surfaces are of 8 bit depth.
836
Assumes dst surface was allocated with the correct dimensions.
837
838
\param src Source surface.
839
\param dst Destination surface.
840
\param cx Horizontal center coordinate.
841
\param cy Vertical center coordinate.
842
\param isin Integer version of sine of angle.
843
\param icos Integer version of cosine of angle.
844
\param flipx Flag indicating horizontal mirroring should be applied.
845
\param flipy Flag indicating vertical mirroring should be applied.
846
*/
847
static void transformSurfaceY(SDL_Surface *src,
848
                              SDL_Surface *dst,
849
                              int cx,
850
                              int cy,
851
                              int isin,
852
                              int icos,
853
                              int flipx,
854
                              int flipy)
855
{
856
    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay;
857
    tColorY *pc, *sp;
858
    int gap;
859
860
    /*
861
    * Variable setup
862
    */
863
    xd = ((src->w - dst->w) << 15);
864
    yd = ((src->h - dst->h) << 15);
865
    ax = (cx << 16) - (icos * cx);
866
    ay = (cy << 16) - (isin * cx);
867
    pc = static_cast<tColorY*>(dst->pixels);
868
    gap = dst->pitch - dst->w;
869
    /*
870
    * Clear surface to colorkey
871
    */
872
    memset(pc, static_cast<int>(_colorkey(src) & 0xff), dst->pitch * dst->h);
873
    /*
874
    * Iterate through destination surface
875
    */
876
    for (y = 0; y < dst->h; y++)
877
    {
878
        dy = cy - y;
879
        sdx = (ax + (isin * dy)) + xd;
880
        sdy = (ay - (icos * dy)) + yd;
881
        for (x = 0; x < dst->w; x++)
882
        {
883
            dx = static_cast<short>(sdx >> 16);
884
            dy = static_cast<short>(sdy >> 16);
885
            if (flipx) dx = (src->w-1)-dx;
886
            if (flipy) dy = (src->h-1)-dy;
887
            if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h))
888
            {
889
                sp = static_cast<tColorY *>(src->pixels);
890
                sp += (src->pitch * dy + dx);
891
                *pc = *sp;
892
            }
893
            sdx += icos;
894
            sdy += isin;
895
            pc++;
896
        }
897
        pc += gap;
898
    }
899
}
900
901
/*!
902
\brief Rotates a 8/16/24/32 bit surface in increments of 90 degrees.
903
904
Specialized 90 degree rotator which rotates a 'src' surface in 90 degree
905
increments clockwise returning a new surface. Faster than rotozoomer since
906
no scanning or interpolation takes place. Input surface must be 8/16/24/32 bit.
907
(code contributed by J. Schiller, improved by C. Allport and A. Schiffler)
908
909
\param src Source surface to rotate.
910
\param numClockwiseTurns Number of clockwise 90 degree turns to apply to the source.
911
912
\returns The new, rotated surface; or nullptr for surfaces with incorrect input format.
913
*/
914
SDL_Surface* rotateSurface90Degrees(SDL_Surface *src,
915
                                    int numClockwiseTurns)
916
{
917
    int row, col, newWidth, newHeight;
918
    int bpp, bpr;
919
    SDL_Surface* dst;
920
    Uint8* srcBuf;
921
    Uint8* dstBuf;
922
    int normalizedClockwiseTurns;
923
924
    /* Has to be a valid surface pointer and be a Nbit surface where n is divisible by 8 */
925
    if (!src ||
926
        !src->format)
927
    {
928
        SDL_SetError("NULL source surface or source surface format");
929
        return nullptr;
930
    }
931
932
    if ((src->format->BitsPerPixel % 8) != 0)
933
    {
934
        SDL_SetError("Invalid source surface bit depth");
935
        return nullptr;
936
    }
937
938
    /* normalize numClockwiseTurns */
939
    normalizedClockwiseTurns = (numClockwiseTurns % 4);
940
    if (normalizedClockwiseTurns < 0)
941
    {
942
        normalizedClockwiseTurns += 4;
943
    }
944
945
    /* If turns are even, our new width/height will be the same as the source surface */
946
    if (normalizedClockwiseTurns % 2)
947
    {
948
        newWidth = src->h;
949
        newHeight = src->w;
950
    }
951
    else
952
    {
953
        newWidth = src->w;
954
        newHeight = src->h;
955
    }
956
957
    dst = SDL_CreateRGBSurface(src->flags,
958
        newWidth,
959
        newHeight,
960
        src->format->BitsPerPixel,
961
        src->format->Rmask,
962
        src->format->Gmask,
963
        src->format->Bmask,
964
        src->format->Amask);
965
    if (!dst)
966
    {
967
        SDL_SetError("Could not create destination surface");
968
        return nullptr;
969
    }
970
971
    if (SDL_MUSTLOCK(src))
972
    {
973
        SDL_LockSurface(src);
974
    }
975
    if (SDL_MUSTLOCK(dst))
976
    {
977
        SDL_LockSurface(dst);
978
    }
979
980
    /* Calculate byte-per-pixel */
981
    bpp = src->format->BitsPerPixel / 8;
982
983
    switch (normalizedClockwiseTurns)
984
    {
985
    case 0: /* Make a copy of the surface */
986
        {
987
            /* Unfortunately SDL_BlitSurface cannot be used to
988
            make a copy of the surface
989
            since it does not preserve alpha. */
990
991
            if (src->pitch == dst->pitch)
992
            {
993
                /* If the pitch is the same for both surfaces,
994
                the memory can be copied all at once. */
995
                memcpy(dst->pixels, src->pixels, (src->h * src->pitch));
996
            }
997
            else
998
            {
999
                /* If the pitch differs, copy each row separately */
1000
                srcBuf = static_cast<Uint8*>(src->pixels);
1001
                dstBuf = static_cast<Uint8*>(dst->pixels);
1002
                bpr = src->w * bpp;
1003
                for (row = 0; row < src->h; row++)
1004
                {
1005
                    memcpy(dstBuf, srcBuf, bpr);
1006
                    srcBuf += src->pitch;
1007
                    dstBuf += dst->pitch;
1008
                }
1009
            }
1010
        }
1011
        break;
1012
1013
        /* rotate clockwise */
1014
    case 1: /* rotated 90 degrees clockwise */
1015
        {
1016
            for (row = 0; row < src->h; ++row)
1017
            {
1018
                srcBuf = static_cast<Uint8*>(src->pixels) +
1019
                    (row * src->pitch);
1020
                dstBuf = static_cast<Uint8*>(dst->pixels) +
1021
                    (dst->w - row - 1) * bpp;
1022
                for (col = 0; col < src->w; ++col)
1023
                {
1024
                    memcpy (dstBuf, srcBuf, bpp);
1025
                    srcBuf += bpp;
1026
                    dstBuf += dst->pitch;
1027
                }
1028
            }
1029
        }
1030
        break;
1031
1032
    case 2: /* rotated 180 degrees clockwise */
1033
        {
1034
            for (row = 0; row < src->h; ++row)
1035
            {
1036
                srcBuf = static_cast<Uint8*>(src->pixels) +
1037
                    (row * src->pitch);
1038
                dstBuf = static_cast<Uint8*>(dst->pixels) +
1039
                    ((dst->h - row - 1) * dst->pitch) + (dst->w - 1) * bpp;
1040
                for (col = 0; col < src->w; ++col)
1041
                {
1042
                    memcpy (dstBuf, srcBuf, bpp);
1043
                    srcBuf += bpp;
1044
                    dstBuf -= bpp;
1045
                }
1046
            }
1047
        }
1048
        break;
1049
1050
    case 3: /* rotated 270 degrees clockwise */
1051
        {
1052
            for (row = 0; row < src->h; ++row)
1053
            {
1054
                srcBuf = static_cast<Uint8*>(src->pixels) + (row * src->pitch);
1055
                dstBuf = static_cast<Uint8*>(dst->pixels) +
1056
                    (row * bpp) + (dst->h * dst->pitch);
1057
                for (col = 0; col < src->w; ++col)
1058
                {
1059
                    memcpy (dstBuf, srcBuf, bpp);
1060
                    srcBuf += bpp;
1061
                    dstBuf -= dst->pitch;
1062
                }
1063
            }
1064
        }
1065
        break;
1066
    default:
1067
        break;
1068
    }
1069
    /* end switch */
1070
1071
    if (SDL_MUSTLOCK(src))
1072
    {
1073
        SDL_UnlockSurface(src);
1074
    }
1075
    if (SDL_MUSTLOCK(dst))
1076
    {
1077
        SDL_UnlockSurface(dst);
1078
    }
1079
1080
    return dst;
1081
}
1082
1083
1084
/*!
1085
\brief Internal target surface sizing function for rotozooms
1086
with trig result return.
1087
1088
\param width The source surface width.
1089
\param height The source surface height.
1090
\param angle The angle to rotate in degrees.
1091
\param zoomx The horizontal scaling factor.
1092
\param zoomy The vertical scaling factor.
1093
\param dstwidth The calculated width of the destination surface.
1094
\param dstheight The calculated height of the destination surface.
1095
\param canglezoom The sine of the angle adjusted by the zoom factor.
1096
\param sanglezoom The cosine of the angle adjusted by the zoom factor.
1097
1098
*/
1099
static void _rotozoomSurfaceSizeTrig(int width,
1100
                                     int height,
1101
                                     double angle,
1102
                                     double zoomx,
1103
                                     double zoomy,
1104
                                     int *dstwidth,
1105
                                     int *dstheight,
1106
                                     double *canglezoom,
1107
                                     double *sanglezoom)
1108
{
1109
    double x, y, cx, cy, sx, sy;
1110
    double radangle;
1111
    int dstwidthhalf, dstheighthalf;
1112
1113
    /*
1114
    * Determine destination width and height by rotating a centered source box
1115
    */
1116
    radangle = angle * (M_PI / 180.0);
1117
    *sanglezoom = sin(radangle);
1118
    *canglezoom = cos(radangle);
1119
    *sanglezoom *= zoomx;
1120
    *canglezoom *= zoomy;
1121
    x = static_cast<double>(width / 2);
1122
    y = static_cast<double>(height / 2);
1123
    cx = *canglezoom * x;
1124
    cy = *canglezoom * y;
1125
    sx = *sanglezoom * x;
1126
    sy = *sanglezoom * y;
1127
1128
    dstwidthhalf = MAX(static_cast<int>(
1129
        ceil(MAX(MAX(MAX(fabs(cx + sy), fabs(cx - sy)),
1130
        fabs(-cx + sy)), fabs(-cx - sy)))), 1);
1131
    dstheighthalf = MAX(static_cast<int>(
1132
        ceil(MAX(MAX(MAX(fabs(sx + cy), fabs(sx - cy)),
1133
        fabs(-sx + cy)), fabs(-sx - cy)))), 1);
1134
    *dstwidth = 2 * dstwidthhalf;
1135
    *dstheight = 2 * dstheighthalf;
1136
}
1137
1138
/*!
1139
\brief Returns the size of the resulting target surface for a
1140
rotozoomSurfaceXY() call.
1141
1142
\param width The source surface width.
1143
\param height The source surface height.
1144
\param angle The angle to rotate in degrees.
1145
\param zoomx The horizontal scaling factor.
1146
\param zoomy The vertical scaling factor.
1147
\param dstwidth The calculated width of the rotozoomed destination surface.
1148
\param dstheight The calculated height of the rotozoomed destination surface.
1149
*/
1150
void rotozoomSurfaceSizeXY(int width,
1151
                           int height,
1152
                           double angle,
1153
                           double zoomx,
1154
                           double zoomy,
1155
                           int *dstwidth,
1156
                           int *dstheight)
1157
{
1158
    double dummy_sanglezoom, dummy_canglezoom;
1159
1160
    _rotozoomSurfaceSizeTrig(width, height, angle, zoomx, zoomy,
1161
        dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
1162
}
1163
1164
/*!
1165
\brief Returns the size of the resulting target surface for a rotozoomSurface()
1166
call.
1167
1168
\param width The source surface width.
1169
\param height The source surface height.
1170
\param angle The angle to rotate in degrees.
1171
\param zoom The scaling factor.
1172
\param dstwidth The calculated width of the rotozoomed destination surface.
1173
\param dstheight The calculated height of the rotozoomed destination surface.
1174
*/
1175
void rotozoomSurfaceSize(int width,
1176
                         int height,
1177
                         double angle,
1178
                         double zoom,
1179
                         int *dstwidth,
1180
                         int *dstheight)
1181
{
1182
    double dummy_sanglezoom, dummy_canglezoom;
1183
1184
    _rotozoomSurfaceSizeTrig(width, height, angle, zoom, zoom,
1185
        dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
1186
}
1187
1188
/*!
1189
\brief Rotates and zooms a surface and optional anti-aliasing.
1190
1191
Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst'
1192
surface. 'angle' is the rotation in degrees and 'zoom' a scaling factor.
1193
If 'smooth' is set then the destination 32bit surface is anti-aliased.
1194
If the surface is not 8bit or 32bit RGBA/ABGR it will be converted into
1195
a 32bit RGBA format on the fly.
1196
1197
\param src The surface to rotozoom.
1198
\param angle The angle to rotate in degrees.
1199
\param zoom The scaling factor.
1200
\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1201
1202
\return The new rotozoomed surface.
1203
*/
1204
SDL_Surface *rotozoomSurface(SDL_Surface *src,
1205
                             double angle,
1206
                             double zoom,
1207
                             int smooth)
1208
{
1209
    return rotozoomSurfaceXY(src, angle, zoom, zoom, smooth);
1210
}
1211
1212
/*!
1213
\brief Rotates and zooms a surface with different horizontal and
1214
vertival scaling factors and optional anti-aliasing.
1215
1216
Rotates and zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1217
'angle' is the rotation in degrees, 'zoomx and 'zoomy' scaling factors.
1218
If 'smooth' is set then the destination 32bit surface is anti-aliased.
1219
If the surface is not 8bit or 32bit RGBA/ABGR it will be converted into
1220
a 32bit RGBA format on the fly.
1221
1222
\param src The surface to rotozoom.
1223
\param angle The angle to rotate in degrees.
1224
\param zoomx The horizontal scaling factor.
1225
\param zoomy The vertical scaling factor.
1226
\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1227
1228
\return The new rotozoomed surface.
1229
*/
1230
SDL_Surface *rotozoomSurfaceXY(SDL_Surface *src,
1231
                               double angle,
1232
                               double zoomx,
1233
                               double zoomy,
1234
                               int smooth)
1235
{
1236
    SDL_Surface *rz_src;
1237
    SDL_Surface *rz_dst;
1238
    double zoominv;
1239
    double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
1240
    int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
1241
    int is32bit;
1242
    int i, src_converted;
1243
    int flipx,flipy;
1244
1245
    /*
1246
    * Sanity check
1247
    */
1248
    if (src == nullptr)
1249
    {
1250
        return (nullptr);
1251
    }
1252
1253
    /*
1254
    * Determine if source surface is 32bit or 8bit
1255
    */
1256
    is32bit = (src->format->BitsPerPixel == 32);
1257
    if ((is32bit) || (src->format->BitsPerPixel == 8))
1258
    {
1259
        /*
1260
        * Use source surface 'as is'
1261
        */
1262
        rz_src = src;
1263
        src_converted = 0;
1264
    }
1265
    else
1266
    {
1267
        /*
1268
        * New source surface is 32bit with a defined RGBA ordering
1269
        */
1270
        rz_src =
1271
            SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1272
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
1273
            0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1274
#else
1275
            0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1276
#endif
1277
            );
1278
1279
        SDL_BlitSurface(src, nullptr, rz_src, nullptr);
1280
1281
        src_converted = 1;
1282
        is32bit = 1;
1283
    }
1284
1285
    /*
1286
    * Sanity check zoom factor
1287
    */
1288
    flipx = (zoomx<0.0);
1289
    if (flipx) zoomx=-zoomx;
1290
    flipy = (zoomy<0.0);
1291
    if (flipy) zoomy=-zoomy;
1292
    if (zoomx < VALUE_LIMIT) zoomx = VALUE_LIMIT;
1293
    if (zoomy < VALUE_LIMIT) zoomy = VALUE_LIMIT;
1294
    zoominv = 65536.0 / (zoomx * zoomx);
1295
1296
    /*
1297
    * Check if we have a rotozoom or just a zoom
1298
    */
1299
    if (fabs(angle) > VALUE_LIMIT)
1300
    {
1301
1302
        /*
1303
        * Angle!=0: full rotozoom
1304
        */
1305
        /*
1306
        * -----------------------
1307
        */
1308
1309
        /* Determine target size */
1310
        _rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoomx, zoomy,
1311
            &dstwidth, &dstheight, &canglezoom, &sanglezoom);
1312
1313
        /*
1314
        * Calculate target factors from sin/cos and zoom
1315
        */
1316
        sanglezoominv = sanglezoom;
1317
        canglezoominv = canglezoom;
1318
        sanglezoominv *= zoominv;
1319
        canglezoominv *= zoominv;
1320
1321
        /* Calculate half size */
1322
        dstwidthhalf = dstwidth / 2;
1323
        dstheighthalf = dstheight / 2;
1324
1325
        /*
1326
        * Alloc space to completely contain the rotated surface
1327
        */
1328
        rz_dst = nullptr;
1329
        if (is32bit)
1330
        {
1331
            /*
1332
            * Target surface is 32bit with source RGBA/ABGR ordering
1333
            */
1334
            rz_dst =
1335
                SDL_CreateRGBSurface(SDL_SWSURFACE,
1336
                dstwidth, dstheight + GUARD_ROWS, 32,
1337
                rz_src->format->Rmask, rz_src->format->Gmask,
1338
                rz_src->format->Bmask, rz_src->format->Amask);
1339
        }
1340
        else
1341
        {
1342
            /*
1343
            * Target surface is 8bit
1344
            */
1345
            rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth,
1346
                dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1347
        }
1348
1349
        /* Check target */
1350
        if (rz_dst == nullptr)
1351
            return nullptr;
1352
1353
        /* Adjust for guard rows */
1354
        rz_dst->h = dstheight;
1355
1356
        /*
1357
        * Lock source surface
1358
        */
1359
        if (SDL_MUSTLOCK(rz_src))
1360
        {
1361
            SDL_LockSurface(rz_src);
1362
        }
1363
1364
        /*
1365
        * Check which kind of surface we have
1366
        */
1367
        if (is32bit)
1368
        {
1369
            /*
1370
            * Call the 32bit transformation routine to do the rotation
1371
            * (using alpha)
1372
            */
1373
            _transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1374
                static_cast<int>(sanglezoominv),
1375
                static_cast<int>(canglezoominv),
1376
                flipx, flipy,
1377
                smooth);
1378
        }
1379
        else
1380
        {
1381
            /*
1382
            * Copy palette and colorkey info
1383
            */
1384
            for (i = 0; i < rz_src->format->palette->ncolors; i++)
1385
            {
1386
                rz_dst->format->palette->colors[i] =
1387
                    rz_src->format->palette->colors[i];
1388
            }
1389
            rz_dst->format->palette->ncolors =
1390
                rz_src->format->palette->ncolors;
1391
            /*
1392
            * Call the 8bit transformation routine to do the rotation
1393
            */
1394
            transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1395
                static_cast<int>(sanglezoominv),
1396
                static_cast<int>(canglezoominv),
1397
                flipx, flipy);
1398
        }
1399
        /*
1400
        * Unlock source surface
1401
        */
1402
        if (SDL_MUSTLOCK(rz_src))
1403
        {
1404
            SDL_UnlockSurface(rz_src);
1405
        }
1406
1407
    }
1408
    else
1409
    {
1410
        /*
1411
        * Angle=0: Just a zoom
1412
        */
1413
        /*
1414
        * --------------------
1415
        */
1416
1417
        /*
1418
        * Calculate target size
1419
        */
1420
        zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy,
1421
            &dstwidth, &dstheight);
1422
1423
        /*
1424
        * Alloc space to completely contain the zoomed surface
1425
        */
1426
        rz_dst = nullptr;
1427
        if (is32bit)
1428
        {
1429
            /*
1430
            * Target surface is 32bit with source RGBA/ABGR ordering
1431
            */
1432
            rz_dst =
1433
                SDL_CreateRGBSurface(SDL_SWSURFACE,
1434
                dstwidth, dstheight + GUARD_ROWS, 32,
1435
                rz_src->format->Rmask, rz_src->format->Gmask,
1436
                rz_src->format->Bmask, rz_src->format->Amask);
1437
        }
1438
        else
1439
        {
1440
            /*
1441
            * Target surface is 8bit
1442
            */
1443
            rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
1444
                dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1445
        }
1446
1447
        /* Check target */
1448
        if (rz_dst == nullptr)
1449
            return nullptr;
1450
1451
        /* Adjust for guard rows */
1452
        rz_dst->h = dstheight;
1453
1454
        /*
1455
        * Lock source surface
1456
        */
1457
        if (SDL_MUSTLOCK(rz_src))
1458
        {
1459
            SDL_LockSurface(rz_src);
1460
        }
1461
1462
        /*
1463
        * Check which kind of surface we have
1464
        */
1465
        if (is32bit)
1466
        {
1467
            /*
1468
            * Call the 32bit transformation routine to do the zooming
1469
            * (using alpha)
1470
            */
1471
            _zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1472
1473
        }
1474
        else
1475
        {
1476
            /*
1477
            * Copy palette and colorkey info
1478
            */
1479
            for (i = 0; i < rz_src->format->palette->ncolors; i++)
1480
            {
1481
                rz_dst->format->palette->colors[i] =
1482
                    rz_src->format->palette->colors[i];
1483
            }
1484
            rz_dst->format->palette->ncolors =
1485
                rz_src->format->palette->ncolors;
1486
1487
            /*
1488
            * Call the 8bit transformation routine to do the zooming
1489
            */
1490
            _zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1491
        }
1492
1493
        /*
1494
        * Unlock source surface
1495
        */
1496
        if (SDL_MUSTLOCK(rz_src))
1497
        {
1498
            SDL_UnlockSurface(rz_src);
1499
        }
1500
    }
1501
1502
    /*
1503
    * Cleanup temp surface
1504
    */
1505
    if (src_converted)
1506
    {
1507
        SDL_FreeSurface(rz_src);
1508
    }
1509
1510
    /*
1511
    * Return destination surface
1512
    */
1513
    return (rz_dst);
1514
}
1515
1516
/*!
1517
\brief Calculates the size of the target surface for a zoomSurface() call.
1518
1519
The minimum size of the target surface is 1. The input factors
1520
can be positive or negative.
1521
1522
\param width The width of the source surface to zoom.
1523
\param height The height of the source surface to zoom.
1524
\param zoomx The horizontal zoom factor.
1525
\param zoomy The vertical zoom factor.
1526
\param dstwidth Pointer to an integer to store the calculated
1527
width of the zoomed target surface.
1528
\param dstheight Pointer to an integer to store the calculated
1529
height of the zoomed target surface.
1530
*/
1531
void zoomSurfaceSize(int width, int height,
1532
                     double zoomx, double zoomy,
1533
                     int *dstwidth, int *dstheight)
1534
{
1535
    /*
1536
    * Make zoom factors positive
1537
    */
1538
    int flipx, flipy;
1539
    flipx = (zoomx<0.0);
1540
    if (flipx) zoomx = -zoomx;
1541
    flipy = (zoomy<0.0);
1542
    if (flipy) zoomy = -zoomy;
1543
1544
    /*
1545
    * Sanity check zoom factors
1546
    */
1547
    if (zoomx < VALUE_LIMIT)
1548
    {
1549
        zoomx = VALUE_LIMIT;
1550
    }
1551
    if (zoomy < VALUE_LIMIT)
1552
    {
1553
        zoomy = VALUE_LIMIT;
1554
    }
1555
1556
    /*
1557
    * Calculate target size
1558
    */
1559
    *dstwidth = static_cast<int>(floor((static_cast<double>(
1560
        width) * zoomx) + 0.5));
1561
    *dstheight = static_cast<int>(floor((static_cast<double>(
1562
        height) * zoomy) + 0.5));
1563
    if (*dstwidth < 1)
1564
    {
1565
        *dstwidth = 1;
1566
    }
1567
    if (*dstheight < 1)
1568
    {
1569
        *dstheight = 1;
1570
    }
1571
}
1572
1573
/*!
1574
\brief Zoom a surface by independent horizontal and vertical
1575
factors with optional smoothing.
1576
1577
Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1578
'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is on
1579
then the destination 32bit surface is anti-aliased. If the surface is not 8bit
1580
or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1581
If zoom factors are negative, the image is flipped on the axes.
1582
1583
\param src The surface to zoom.
1584
\param zoomx The horizontal zoom factor.
1585
\param zoomy The vertical zoom factor.
1586
\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1587
1588
\return The new, zoomed surface.
1589
*/
1590
SDL_Surface *zoomSurface(SDL_Surface *src,
1591
                         double zoomx,
1592
                         double zoomy,
1593
                         int smooth)
1594
{
1595
    SDL_Surface *rz_src;
1596
    SDL_Surface *rz_dst;
1597
    int dstwidth, dstheight;
1598
    int is32bit;
1599
    int i, src_converted;
1600
    int flipx, flipy;
1601
1602
    /*
1603
    * Sanity check
1604
    */
1605
    if (src == nullptr)
1606
        return (nullptr);
1607
1608
    /*
1609
    * Determine if source surface is 32bit or 8bit
1610
    */
1611
    is32bit = (src->format->BitsPerPixel == 32);
1612
    if ((is32bit) || (src->format->BitsPerPixel == 8))
1613
    {
1614
        /*
1615
        * Use source surface 'as is'
1616
        */
1617
        rz_src = src;
1618
        src_converted = 0;
1619
    }
1620
    else
1621
    {
1622
        /*
1623
        * New source surface is 32bit with a defined RGBA ordering
1624
        */
1625
        rz_src =
1626
            SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1627
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
1628
            0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1629
#else
1630
            0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1631
#endif
1632
            );
1633
        if (rz_src == nullptr)
1634
        {
1635
            return nullptr;
1636
        }
1637
        SDL_BlitSurface(src, nullptr, rz_src, nullptr);
1638
        src_converted = 1;
1639
        is32bit = 1;
1640
    }
1641
1642
    flipx = (zoomx<0.0);
1643
    if (flipx) zoomx = -zoomx;
1644
    flipy = (zoomy<0.0);
1645
    if (flipy) zoomy = -zoomy;
1646
1647
    /* Get size if target */
1648
    zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1649
1650
    /*
1651
    * Alloc space to completely contain the zoomed surface
1652
    */
1653
    rz_dst = nullptr;
1654
    if (is32bit)
1655
    {
1656
        /*
1657
        * Target surface is 32bit with source RGBA/ABGR ordering
1658
        */
1659
        rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
1660
            dstwidth, dstheight + GUARD_ROWS, 32,
1661
            rz_src->format->Rmask, rz_src->format->Gmask,
1662
            rz_src->format->Bmask, rz_src->format->Amask);
1663
    }
1664
    else
1665
    {
1666
        /*
1667
        * Target surface is 8bit
1668
        */
1669
        rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
1670
            dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1671
    }
1672
1673
    /* Check target */
1674
    if (rz_dst == nullptr)
1675
    {
1676
        /*
1677
        * Cleanup temp surface
1678
        */
1679
        if (src_converted)
1680
        {
1681
            SDL_FreeSurface(rz_src);
1682
        }
1683
        return nullptr;
1684
    }
1685
1686
    /* Adjust for guard rows */
1687
    rz_dst->h = dstheight;
1688
1689
    /*
1690
    * Lock source surface
1691
    */
1692
    if (SDL_MUSTLOCK(rz_src))
1693
    {
1694
        SDL_LockSurface(rz_src);
1695
    }
1696
1697
    /*
1698
    * Check which kind of surface we have
1699
    */
1700
    if (is32bit)
1701
    {
1702
        /*
1703
        * Call the 32bit transformation routine to do the zooming (using alpha)
1704
        */
1705
        _zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1706
    }
1707
    else
1708
    {
1709
        /*
1710
        * Copy palette and colorkey info
1711
        */
1712
        for (i = 0; i < rz_src->format->palette->ncolors; i++)
1713
        {
1714
            rz_dst->format->palette->colors[i] =
1715
                rz_src->format->palette->colors[i];
1716
        }
1717
        rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1718
        /*
1719
        * Call the 8bit transformation routine to do the zooming
1720
        */
1721
        _zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1722
    }
1723
    /*
1724
    * Unlock source surface
1725
    */
1726
    if (SDL_MUSTLOCK(rz_src))
1727
    {
1728
        SDL_UnlockSurface(rz_src);
1729
    }
1730
1731
    /*
1732
    * Cleanup temp surface
1733
    */
1734
    if (src_converted)
1735
    {
1736
        SDL_FreeSurface(rz_src);
1737
    }
1738
1739
    /*
1740
    * Return destination surface
1741
    */
1742
    return (rz_dst);
1743
}
1744
1745
/*!
1746
\brief Shrink a surface by an integer ratio using averaging.
1747
1748
Shrinks a 32bit or 8bit 'src' surface to a newly created 'dst' surface.
1749
'factorx' and 'factory' are the shrinking ratios (i.e. 2=1/2 the size,
1750
3=1/3 the size, etc.) The destination surface is antialiased by averaging
1751
the source box RGBA or Y information. If the surface is not 8bit
1752
or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1753
The input surface is not modified. The output surface is newly allocated.
1754
1755
\param src The surface to shrink.
1756
\param factorx The horizontal shrinking ratio.
1757
\param factory The vertical shrinking ratio.
1758
1759
\return The new, shrunken surface.
1760
*/
1761
/*@nullptr@*/
1762
SDL_Surface *shrinkSurface(SDL_Surface *src,
1763
                           int factorx,
1764
                           int factory)
1765
{
1766
    int result;
1767
    SDL_Surface *rz_src;
1768
    SDL_Surface *rz_dst = nullptr;
1769
    int dstwidth, dstheight;
1770
    int is32bit;
1771
    int i, src_converted = 0;
1772
    int haveError = 0;
1773
1774
    /*
1775
    * Sanity check
1776
    */
1777
    if (src == nullptr)
1778
    {
1779
        return (nullptr);
1780
    }
1781
1782
    /*
1783
    * Determine if source surface is 32bit or 8bit
1784
    */
1785
    is32bit = (src->format->BitsPerPixel == 32);
1786
    if ((is32bit) || (src->format->BitsPerPixel == 8))
1787
    {
1788
        /*
1789
        * Use source surface 'as is'
1790
        */
1791
        rz_src = src;
1792
        src_converted = 0;
1793
    }
1794
    else
1795
    {
1796
        /*
1797
        * New source surface is 32bit with a defined RGBA ordering
1798
        */
1799
        rz_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1800
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
1801
            0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1802
#else
1803
            0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1804
#endif
1805
            );
1806
        if (rz_src==nullptr)
1807
        {
1808
            haveError = 1;
1809
            goto exitShrinkSurface;
1810
        }
1811
1812
        SDL_BlitSurface(src, nullptr, rz_src, nullptr);
1813
        src_converted = 1;
1814
        is32bit = 1;
1815
    }
1816
1817
    /*
1818
    * Lock the surface
1819
    */
1820
    if (SDL_MUSTLOCK(rz_src))
1821
    {
1822
        if (SDL_LockSurface(rz_src) < 0)
1823
        {
1824
            haveError = 1;
1825
            goto exitShrinkSurface;
1826
        }
1827
    }
1828
1829
    /* Get size for target */
1830
    dstwidth=rz_src->w/factorx;
1831
    while (dstwidth*factorx>rz_src->w)
1832
    {
1833
        dstwidth--;
1834
    }
1835
    dstheight=rz_src->h/factory;
1836
    while (dstheight*factory>rz_src->h)
1837
    {
1838
        dstheight--;
1839
    }
1840
1841
    /*
1842
    * Alloc space to completely contain the shrunken surface
1843
    * (with added guard rows)
1844
    */
1845
    if (is32bit==1)
1846
    {
1847
        /*
1848
        * Target surface is 32bit with source RGBA/ABGR ordering
1849
        */
1850
        rz_dst = SDL_CreateRGBSurface(
1851
            SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
1852
            rz_src->format->Rmask, rz_src->format->Gmask,
1853
            rz_src->format->Bmask, rz_src->format->Amask);
1854
    }
1855
    else
1856
    {
1857
        /*
1858
        * Target surface is 8bit
1859
        */
1860
        rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
1861
            dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1862
    }
1863
1864
    /* Check target */
1865
    if (rz_dst == nullptr)
1866
    {
1867
        haveError = 1;
1868
        goto exitShrinkSurface;
1869
    }
1870
1871
    /* Adjust for guard rows */
1872
    rz_dst->h = dstheight;
1873
1874
    /*
1875
    * Check which kind of surface we have
1876
    */
1877
    if (is32bit==1)
1878
    {
1879
        /*
1880
        * Call the 32bit transformation routine to do the shrinking (using alpha)
1881
        */
1882
        result = _shrinkSurfaceRGBA(rz_src, rz_dst, factorx, factory);
1883
        if ((result!=0) || (rz_dst==nullptr))
1884
        {
1885
            haveError = 1;
1886
            goto exitShrinkSurface;
1887
        }
1888
    }
1889
    else
1890
    {
1891
        /*
1892
        * Copy palette and colorkey info
1893
        */
1894
        for (i = 0; i < rz_src->format->palette->ncolors; i++)
1895
        {
1896
            rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1897
        }
1898
        rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1899
        /*
1900
        * Call the 8bit transformation routine to do the shrinking
1901
        */
1902
        result = _shrinkSurfaceY(rz_src, rz_dst, factorx, factory);
1903
        if (result!=0)
1904
        {
1905
            haveError = 1;
1906
            goto exitShrinkSurface;
1907
        }
1908
    }
1909
1910
exitShrinkSurface:
1911
    if (rz_src!=nullptr)
1912
    {
1913
        /*
1914
        * Unlock source surface
1915
        */
1916
        if (SDL_MUSTLOCK(rz_src))
1917
        {
1918
            SDL_UnlockSurface(rz_src);
1919
        }
1920
1921
        /*
1922
        * Cleanup temp surface
1923
        */
1924
        if (src_converted==1)
1925
        {
1926
            SDL_FreeSurface(rz_src);
1927
        }
1928
    }
1929
1930
    /* Check error state; maybe need to cleanup destination */
1931
    if (haveError==1)
1932
    {
1933
        if (rz_dst!=nullptr)
1934
        {
1935
            SDL_FreeSurface(rz_dst);
1936
        }
1937
        rz_dst=nullptr;
1938
    }
1939
1940
    /*
1941
    * Return destination surface
1942
    */
1943
    return (rz_dst);
1944
}