forked from stepmania/stepmania
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRageSurfaceUtils_Zoom.cpp
204 lines (174 loc) · 6.55 KB
/
RageSurfaceUtils_Zoom.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include "global.h"
#include "RageSurfaceUtils_Zoom.h"
#include "RageMath.hpp"
#include "RageSurface.h"
#include "RageSurfaceUtils.h"
#include "RageUtil.h"
#include <vector>
using namespace std;
/* Coordinate 0x0 represents the exact top-left corner of a bitmap. .5x.5
* represents the center of the top-left pixel; 1x1 is the center of the top
* square of pixels.
*
* (Look at a grid: map coordinates to the lines, not the squares between the
* lines.) */
static void InitVectors( vector<int> &s0, vector<int> &s1, vector<uint32_t> &percent, int src, int dst )
{
if( src >= dst )
{
float sx = float(src) / dst;
for( int x = 0; x < dst; x++ )
{
/* sax is the exact (floating-point) x coordinate in the source
* that the destination pixel at x should from. For example, if we're
* going 512->256, then dst[0] should come from the pixels from 0..1 and
* 1..2, so sax[0] is 1. sx is the total number of pixels, so sx/2 is the
* distance from the start of the sample to its center. */
const float sax = sx*x + sx/2.0f;
/* sx/2 is the distance from the start of the sample to the center;
* sx/4 is the distance from the center of the sample to the center of
* either pixel. */
const float xstep = sx/4.0f;
// source x coordinates of left and right pixels to sample
s0.push_back(int(sax-xstep));
s1.push_back(int(sax+xstep));
if( s0[x] == s1[x] )
{
/* If the sampled pixels happen to be the same, the distance
* will be 0. Avoid division by zero. */
percent.push_back( 1<<24 );
} else {
const int xdist = s1[x] - s0[x];
// fleft is the left pixel sampled; +.5 is the center:
const float fleft = s0[x] + .5f;
/* sax is somewhere between the centers of both sampled
* pixels; find the percentage: */
const float p = (1.0f - (sax - fleft) / xdist) * 16777216.0f;
percent.push_back( uint32_t(p) );
}
}
}
else
{
/* Fencepost: If we have source:
* abcd
* and dest:
* xyz
* then we want x to be sampled entirely from a, and z entirely from d;
* the inner pixels are interpolated. (This behavior mimics Photoshop's
* resize.) */
float sx = float(src-1) / (dst-1);
for( int x = 0; x < dst; x++ )
{
const float sax = sx*x;
// source x coordinates of left and right pixels to sample
s0.push_back( Rage::clamp(int(sax), 0, src-1));
s1.push_back( Rage::clamp(int(sax+1), 0, src-1) );
const float p = (1.0f - (sax - std::floor(sax))) * 16777216.0f;
percent.push_back( uint32_t(p) );
}
}
}
static void ZoomSurface( const RageSurface * src, RageSurface * dst )
{
/* For each destination coordinate, two source rows, two source columns
* and the percentage of the first row and first column: */
vector<int> esx0, esx1, esy0, esy1;
vector<uint32_t> ex0, ey0;
InitVectors( esx0, esx1, ex0, src->w, dst->w );
InitVectors( esy0, esy1, ey0, src->h, dst->h );
// This is where all of the real work is done.
const uint8_t *sp = (uint8_t *) src->pixels;
const int height = dst->h;
const int width = dst->w;
for( int y = 0; y < height; y++ )
{
uint8_t *dp = (uint8_t *) (dst->pixels + dst->pitch*y);
/* current source pointer and next source pointer (first and second
* rows sampled for this row): */
const uint8_t *csp = sp + esy0[y] * src->pitch;
const uint8_t *ncsp = sp + esy1[y] * src->pitch;
for( int x = 0; x < width; x++ )
{
// Grab pointers to the sampled pixels:
const uint8_t *c00 = csp + esx0[x]*4;
const uint8_t *c01 = csp + esx1[x]*4;
const uint8_t *c10 = ncsp + esx0[x]*4;
const uint8_t *c11 = ncsp + esx1[x]*4;
for( int c = 0; c < 4; ++c )
{
uint32_t x0 = uint32_t(c00[c]) * ex0[x];
x0 += uint32_t(c01[c]) * (16777216 - ex0[x]);
x0 >>= 24;
uint32_t x1 = uint32_t(c10[c]) * ex0[x];
x1 += uint32_t(c11[c]) * (16777216 - ex0[x]);
x1 >>= 24;
const uint32_t res = ((x0 * ey0[y]) + (x1 * (16777216-ey0[y])) + 8388608) >> 24;
dp[c] = uint8_t(res);
}
// Advance destination pointer.
dp += 4;
}
}
}
void RageSurfaceUtils::Zoom( RageSurface *&src, int dstwidth, int dstheight )
{
ASSERT_M( dstwidth > 0, fmt::sprintf("%i",dstwidth) );
ASSERT_M( dstheight > 0, fmt::sprintf("%i",dstheight) );
if( src == nullptr )
return;
if( src->w == dstwidth && src->h == dstheight )
return;
// resize currently only does RGBA8888
if( src->fmt.BytesPerPixel != 4 )
{
RageSurfaceUtils::ConvertSurface( src, src->w, src->h, 32,
0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
}
while( src->w != dstwidth || src->h != dstheight )
{
float xscale = float(dstwidth)/src->w;
float yscale = float(dstheight)/src->h;
/* Our filter is a simple linear filter, so it can't scale to less than
* 1:2 or more than 2:1 very well. If we need to go beyond that, do it
* iteratively. */
xscale = Rage::clamp( xscale, .5f, 2.0f );
yscale = Rage::clamp( yscale, .5f, 2.0f );
int target_width = std::lrint( src->w*xscale );
int target_height = std::lrint( src->h*yscale );
RageSurface *dst =
CreateSurface(target_width, target_height, 32,
src->format->Rmask, src->format->Gmask,
src->format->Bmask, src->format->Amask);
ZoomSurface( src, dst );
delete src;
src = dst;
}
}
/*
* (c) A. Schiffler, Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* This is based on code from SDL_rotozoom, under the above license with
* permission from Andreas Schiffler.
*/