ScummVM API documentation
2PassScale.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef QDENGINE_QDCORE_UTIL_2_PASS_SCALE_H
23 #define QDENGINE_QDCORE_UTIL_2_PASS_SCALE_H
24 
25 #include "qdengine/qdcore/util/Filters.h"
26 
27 #define TRACE(a)
28 #define ASSERT(a)
29 
30 namespace QDEngine {
31 
32 namespace scl {
33 
34 typedef struct {
35  double *weights; // Normalized weights of neighboring pixels
36  int left, right; // Bounds of source pixels window
37 } ContributionType; // Contirbution information for a single pixel
38 
39 typedef struct {
40  ContributionType *contribRow; // Row (or column) of contribution weights
41  uint32 windowSize, // Filter window size (of affecting source pixels)
42  lineLength; // Length of line (no. or rows / cols)
43 } LineContribType; // Contribution information for an entire line (row or column)
44 
45 template <class FilterClass>
46 class C2PassScale {
47 public:
48 
49  C2PassScale() : _temp_buffer(65536, 0), _weights_buffer(16384, 0.0), _contribution_buffer(500) { }
50  virtual ~C2PassScale() { }
51 
52  uint32 *scale(uint32 *pOrigImage, uint32 uOrigWidth, uint32 uOrigHeight, uint32 *pDstImage, uint32 uNewWidth, uint32 uNewHeight);
53 
54 private:
55 
56 
57  Std::vector<uint32> _temp_buffer;
58 
59  Std::vector<double> _weights_buffer;
60  Std::vector<ContributionType> _contribution_buffer;
61 
62  LineContribType *allocContributions(uint32 uLineLength, uint32 uWindowSize);
63  LineContribType *calcContributions(uint32 uLineSize, uint32 uSrcSize, double dScale);
64 
65  void scaleRow(uint32 *pSrc, uint32 uSrcWidth, uint32 *pRes, uint32 uResWidth, uint32 uRow, LineContribType *Contrib);
66  void horizScale(uint32 *pSrc, uint32 uSrcWidth, uint32 uSrcHeight, uint32 *pDst, uint32 uResWidth, uint32 uResHeight);
67  void scaleCol(uint32 *pSrc, uint32 uSrcWidth, uint32 *pRes, uint32 uResWidth, uint32 uResHeight, uint32 uCol, LineContribType *Contrib);
68  void vertScale(uint32 *pSrc, uint32 uSrcWidth, uint32 uSrcHeight, uint32 *pDst, uint32 uResWidth, uint32 uResHeight);
69 
70  static inline byte make_r(uint32 col) {
71  return reinterpret_cast<byte *>(&col)[2];
72  }
73  static inline byte make_g(uint32 col) {
74  return reinterpret_cast<byte *>(&col)[1];
75  }
76  static inline byte make_b(uint32 col) {
77  return reinterpret_cast<byte *>(&col)[0];
78  }
79  static inline byte make_a(uint32 col) {
80  return reinterpret_cast<byte *>(&col)[3];
81  }
82 
83  static inline uint32 make_rgba(byte r, byte g, byte b, byte a) {
84  return (r << 16) | (g << 8) | (b << 0) | (a << 24);
85  }
86 };
87 
88 template<class FilterClass>
89 LineContribType *C2PassScale<FilterClass>::allocContributions(uint32 uLineLength, uint32 uWindowSize) {
90  static LineContribType line_ct;
91 
92  line_ct.windowSize = uWindowSize;
93  line_ct.lineLength = uLineLength;
94 
95  if (_contribution_buffer.size() < uLineLength)
96  _contribution_buffer.resize(uLineLength);
97 
98  line_ct.contribRow = &*_contribution_buffer.begin();
99 
100  if (_weights_buffer.size() < uLineLength * uWindowSize)
101  _weights_buffer.resize(uLineLength * uWindowSize);
102 
103  double *p = &*_weights_buffer.begin();
104 
105  for (uint32 u = 0; u < uLineLength; u++) {
106  line_ct.contribRow[u].weights = p;
107  p += uWindowSize;
108  }
109  return &line_ct;
110 }
111 
112 template <class FilterClass>
113 LineContribType *C2PassScale<FilterClass>::calcContributions(uint32 uLineSize, uint32 uSrcSize, double dScale) {
114  FilterClass curFilter;
115 
116  double dWidth;
117  double dFScale = 1.0;
118  double dFilterWidth = curFilter.getWidth();
119 
120  if (dScale < 1.0) { // Minification
121  dWidth = dFilterWidth / dScale;
122  dFScale = dScale;
123  } else { // Magnification
124  dWidth = dFilterWidth;
125  }
126 
127  // Window size is the number of sampled pixels
128  int iWindowSize = 2 * (int)ceil(dWidth) + 1;
129 
130  // Allocate a new line contributions strucutre
131  LineContribType *res = allocContributions(uLineSize, iWindowSize);
132 
133  for (uint32 u = 0; u < uLineSize; u++) {
134  // Scan through line of contributions
135  double dCenter = (double)u / dScale; // Reverse mapping
136  // Find the significant edge points that affect the pixel
137  int iLeft = MAX(0, (int)floor(dCenter - dWidth));
138  int iRight = MIN((int)ceil(dCenter + dWidth), int(uSrcSize) - 1);
139 
140  // Cut edge points to fit in filter window in case of spill-off
141  if (iRight - iLeft + 1 > iWindowSize) {
142  if (iLeft < (int(uSrcSize) - 1 / 2)) {
143  iLeft++;
144  } else {
145  iRight--;
146  }
147  }
148 
149  res->contribRow[u].left = iLeft;
150  res->contribRow[u].right = iRight;
151 
152  double dTotalWeight = 0.0; // Zero sum of weights
153  for (int iSrc = iLeft; iSrc <= iRight; iSrc++) {
154  // Calculate weights
155  dTotalWeight += (res->contribRow[u].weights[iSrc - iLeft] = dFScale * curFilter.filter(dFScale * (dCenter - (double)iSrc)));
156  }
157  ASSERT(dTotalWeight >= 0.0); // An error in the filter function can cause this
158  if (dTotalWeight > 0.0) {
159  // Normalize weight of neighbouring points
160  for (int iSrc = iLeft; iSrc <= iRight; iSrc++) {
161  // Normalize point
162  res->contribRow[u].weights[iSrc - iLeft] /= dTotalWeight;
163  }
164  }
165  }
166  return res;
167 }
168 
169 template <class FilterClass>
170 void C2PassScale<FilterClass>::scaleRow(uint32 *pSrc, uint32 uSrcWidth, uint32 *pRes, uint32 uResWidth, uint32 uRow, LineContribType *contrib) {
171  uint32 *pSrcRow = &(pSrc[uRow * uSrcWidth]);
172  uint32 *pDstRow = &(pRes[uRow * uResWidth]);
173  for (uint32 x = 0; x < uResWidth; x++) {
174  // Loop through row
175  double dr = 0.0;
176  double dg = 0.0;
177  double db = 0.0;
178  double da = 0.0;
179 
180  int iLeft = contrib->contribRow[x].left; // Retrieve left boundries
181  int iRight = contrib->contribRow[x].right; // Retrieve right boundries
182  for (int i = iLeft; i <= iRight; i++) {
183  // Scan between boundries
184  // Accumulate weighted effect of each neighboring pixel
185  dr += contrib->contribRow[x].weights[i - iLeft] * (double)(make_r(pSrcRow[i]));
186  dg += contrib->contribRow[x].weights[i - iLeft] * (double)(make_g(pSrcRow[i]));
187  db += contrib->contribRow[x].weights[i - iLeft] * (double)(make_b(pSrcRow[i]));
188  da += contrib->contribRow[x].weights[i - iLeft] * (double)(make_a(pSrcRow[i]));
189  }
190 
191  uint32 r = round(dr);
192  uint32 g = round(dg);
193  uint32 b = round(db);
194  uint32 a = round(da);
195 
196  pDstRow[x] = make_rgba(r, g, b, a); // Place result in destination pixel
197  }
198 }
199 
200 template <class FilterClass>
201 void C2PassScale<FilterClass>::horizScale(uint32 *pSrc, uint32 uSrcWidth, uint32 uSrcHeight, uint32 *pDst, uint32 uResWidth, uint32 uResHeight) {
202  TRACE("Performing horizontal scaling...\n");
203  if (uResWidth == uSrcWidth) {
204  // No scaling required, just copy
205  memcpy(pDst, pSrc, sizeof(uint32) * uSrcHeight * uSrcWidth);
206  return;
207  }
208  // Allocate and calculate the contributions
209  LineContribType *contrib = calcContributions(uResWidth, uSrcWidth, double(uResWidth) / double(uSrcWidth));
210  for (uint32 u = 0; u < uResHeight; u++)
211  scaleRow(pSrc, uSrcWidth, pDst, uResWidth, u, contrib); // Scale each row
212 }
213 
214 template <class FilterClass>
215 void C2PassScale<FilterClass>::scaleCol(uint32 *pSrc, uint32 uSrcWidth, uint32 *pRes, uint32 uResWidth, uint32 uResHeight, uint32 uCol, LineContribType *contrib) {
216  for (uint32 y = 0; y < uResHeight; y++) {
217  // Loop through column
218  double dr = 0.0;
219  double dg = 0.0;
220  double db = 0.0;
221  double da = 0.0;
222 
223  int iLeft = contrib->contribRow[y].left; // Retrieve left boundries
224  int iRight = contrib->contribRow[y].right; // Retrieve right boundries
225  for (int i = iLeft; i <= iRight; i++) {
226  // Scan between boundries
227  // Accumulate weighted effect of each neighboring pixel
228  uint32 pCurSrc = pSrc[i * uSrcWidth + uCol];
229  dr += contrib->contribRow[y].weights[i - iLeft] * (double)(make_r(pCurSrc));
230  dg += contrib->contribRow[y].weights[i - iLeft] * (double)(make_g(pCurSrc));
231  db += contrib->contribRow[y].weights[i - iLeft] * (double)(make_b(pCurSrc));
232  da += contrib->contribRow[y].weights[i - iLeft] * (double)(make_a(pCurSrc));
233  }
234 
235  uint32 r = round(dr);
236  uint32 g = round(dg);
237  uint32 b = round(db);
238  uint32 a = round(da);
239 
240  pRes[y * uResWidth + uCol] = make_rgba(r, g, b, a); // Place result in destination pixel
241  }
242 }
243 
244 template <class FilterClass>
245 void C2PassScale<FilterClass>::vertScale(uint32 *pSrc, uint32 uSrcWidth, uint32 uSrcHeight, uint32 *pDst, uint32 uResWidth, uint32 uResHeight) {
246  TRACE("Performing vertical scaling...");
247 
248  if (uSrcHeight == uResHeight) {
249  // No scaling required, just copy
250  memcpy(pDst, pSrc, sizeof(uint32) * uSrcHeight * uSrcWidth);
251  return;
252  }
253  // Allocate and calculate the contributions
254  LineContribType *Contrib = calcContributions(uResHeight, uSrcHeight, double(uResHeight) / double(uSrcHeight));
255  for (uint32 u = 0; u < uResWidth; u++)
256  scaleCol(pSrc, uSrcWidth, pDst, uResWidth, uResHeight, u, Contrib); // Scale each column
257 }
258 
259 template <class FilterClass>
260 uint32 *C2PassScale<FilterClass>::scale(uint32 *pOrigImage, uint32 uOrigWidth, uint32 uOrigHeight, uint32 *pDstImage, uint32 uNewWidth, uint32 uNewHeight) {
261  if (_temp_buffer.size() < uNewWidth * uOrigHeight)
262  _temp_buffer.resize(uNewWidth * uOrigHeight);
263 
264  uint32 *pTemp = reinterpret_cast<uint32 *>(&*_temp_buffer.begin());
265  horizScale(pOrigImage, uOrigWidth, uOrigHeight, pTemp, uNewWidth, uOrigHeight);
266 
267  // Scale temporary image vertically into result image
268  vertScale(pTemp, uNewWidth, uOrigHeight, pDstImage, uNewWidth, uNewHeight);
269 
270  return pDstImage;
271 }
272 
273 } // namespace scl
274 
275 } // namespace QDEngine
276 
277 #endif // QDENGINE_QDCORE_UTIL_2_PASS_SCALE_H
Definition: 2PassScale.h:34
Definition: 2PassScale.h:46
iterator begin()
Definition: array.h:374
Базовый класс для игровых ресурсов.
Definition: console.h:28
Definition: 2PassScale.h:39
size_type size() const
Definition: array.h:315
void resize(size_type newSize)
Definition: array.h:411
T MIN(T a, T b)
Definition: util.h:59
T MAX(T a, T b)
Definition: util.h:62