openMSX
SaI2xScaler.cc
Go to the documentation of this file.
1 // 2xSaI is Copyright (c) 1999-2001 by Derek Liauw Kie Fa.
2 // http://elektron.its.tudelft.nl/~dalikifa/
3 // 2xSaI is free under GPL.
4 //
5 // Modified for use in openMSX by Maarten ter Huurne.
6 
7 #include "SaI2xScaler.hh"
8 #include "FrameSource.hh"
9 #include "ScalerOutput.hh"
10 #include "vla.hh"
11 #include "build-info.hh"
12 #include <cassert>
13 #include <cstdint>
14 
15 namespace openmsx {
16 
17 template <class Pixel>
18 SaI2xScaler<Pixel>::SaI2xScaler(const PixelOperations<Pixel>& pixelOps_)
19  : Scaler2<Pixel>(pixelOps_)
20  , pixelOps(pixelOps_)
21 {
22 }
23 
24 template <class Pixel>
25 void SaI2xScaler<Pixel>::scaleBlank1to2(
26  FrameSource& src, unsigned srcStartY, unsigned srcEndY,
27  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
28 {
29  unsigned dstHeight = dst.getHeight();
30  unsigned stopDstY = (dstEndY == dstHeight)
31  ? dstEndY : dstEndY - 2;
32  unsigned srcY = srcStartY, dstY = dstStartY;
33  for (/* */; dstY < stopDstY; srcY += 1, dstY += 2) {
34  auto color = src.getLineColor<Pixel>(srcY);
35  dst.fillLine(dstY + 0, color);
36  dst.fillLine(dstY + 1, color);
37  }
38  if (dstY != dstHeight) {
39  unsigned nextLineWidth = src.getLineWidth(srcY + 1);
40  assert(src.getLineWidth(srcY) == 1);
41  assert(nextLineWidth != 1);
42  this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
43  dst, dstY, dstEndY);
44  }
45 }
46 
47 template <class Pixel>
48 inline Pixel SaI2xScaler<Pixel>::blend(Pixel p1, Pixel p2)
49 {
50  return pixelOps.template blend<1, 1>(p1, p2);
51 }
52 
53 template <class Pixel>
54 void SaI2xScaler<Pixel>::scaleLine1on2(
55  const Pixel* __restrict srcLine0, const Pixel* __restrict srcLine1,
56  const Pixel* __restrict srcLine2, const Pixel* __restrict srcLine3,
57  Pixel* __restrict dstUpper, Pixel* __restrict dstLower,
58  unsigned srcWidth)
59 {
60  // TODO: Scale border pixels as well.
61  for (unsigned x = 0; x < srcWidth; x++) {
62  // Map of the pixels:
63  // I|E F|J
64  // G|A B|K
65  // H|C D|L
66  // M|N O|P
67 
68  unsigned xl = x ? x - 1 : 0;
69  unsigned xr = std::min(x + 1, srcWidth - 1);
70  unsigned xrr = std::min(x + 2, srcWidth - 1);
71 
72  // TODO: Possible performance improvements:
73  // - Play with order of fetching (effect on data cache).
74  // - Try not fetching at all (using srcLineN[x] in algorithm).
75  // - Try rotating the fetched colors (either in vars or in array).
76  Pixel colorI = srcLine0[xl];
77  Pixel colorE = srcLine0[x];
78  Pixel colorF = srcLine0[xr];
79  Pixel colorJ = srcLine0[xrr];
80 
81  Pixel colorG = srcLine1[xl];
82  Pixel colorA = srcLine1[x];
83  Pixel colorB = srcLine1[xr];
84  Pixel colorK = srcLine1[xrr];
85 
86  Pixel colorH = srcLine2[xl];
87  Pixel colorC = srcLine2[x];
88  Pixel colorD = srcLine2[xr];
89  Pixel colorL = srcLine2[xrr];
90 
91  Pixel colorM = srcLine3[xl];
92  Pixel colorN = srcLine3[x];
93  Pixel colorO = srcLine3[xr];
94  //Pixel colorP = srcLine3[xrr];
95 
96  Pixel product, product1, product2;
97  if (colorA == colorD && colorB != colorC) {
98  product = ((colorA == colorE && colorB == colorL) ||
99  (colorA == colorC && colorA == colorF &&
100  colorB != colorE && colorB == colorJ))
101  ? colorA
102  : blend(colorA, colorB);
103  product1 = ((colorA == colorG && colorC == colorO) ||
104  (colorA == colorB && colorA == colorH &&
105  colorG != colorC && colorC == colorM))
106  ? colorA
107  : blend(colorA, colorC);
108  product2 = colorA;
109  } else if (colorB == colorC && colorA != colorD) {
110  product = ((colorB == colorF && colorA == colorH) ||
111  (colorB == colorE && colorB == colorD &&
112  colorA != colorF && colorA == colorI))
113  ? colorB
114  : blend(colorA, colorB);
115  product1 = ((colorC == colorH && colorA == colorF) ||
116  (colorC == colorG && colorC == colorD &&
117  colorA != colorH && colorA == colorI))
118  ? colorC
119  : blend(colorA, colorC);
120  product2 = colorB;
121  } else if (colorA == colorD && colorB == colorC) {
122  if (colorA == colorB) {
123  product = product1 = product2 = colorA;
124  } else {
125  int r = 0;
126  if (colorE == colorG) {
127  if (colorA == colorE) r--;
128  else if (colorB == colorE) r++;
129  }
130  if (colorF == colorK) {
131  if (colorA == colorF) r--;
132  else if (colorB == colorF) r++;
133  }
134  if (colorH == colorN) {
135  if (colorA == colorH) r--;
136  else if (colorB == colorH) r++;
137  }
138  if (colorL == colorO) {
139  if (colorA == colorL) r--;
140  else if (colorB == colorL) r++;
141  }
142  product = product1 = blend(colorA, colorB);
143  product2 = r > 0 ? colorA : (r < 0 ? colorB : product);
144  }
145  } else {
146  product = (colorA == colorC && colorA == colorF &&
147  colorB != colorE && colorB == colorJ)
148  ? colorA
149  : ((colorB == colorE && colorB == colorD &&
150  colorA != colorF && colorA == colorI)
151  ? colorB
152  : blend(colorA, colorB));
153  product1 = (colorA == colorB && colorA == colorH &&
154  colorG != colorC && colorC == colorM)
155  ? colorA
156  : ((colorC == colorG && colorC == colorD &&
157  colorA != colorH && colorA == colorI)
158  ? colorC
159  : blend(colorA, colorC));
160  product2 = blend( // TODO: Quad-blend may be better?
161  blend(colorA, colorB),
162  blend(colorC, colorD));
163  }
164 
165  dstUpper[x * 2 + 0] = colorA;
166  dstUpper[x * 2 + 1] = product;
167  dstLower[x * 2 + 0] = product1;
168  dstLower[x * 2 + 1] = product2;
169  }
170 }
171 
172 template <class Pixel>
173 void SaI2xScaler<Pixel>::scaleLine1on1(
174  const Pixel* __restrict srcLine0, const Pixel* __restrict srcLine1,
175  const Pixel* __restrict srcLine2, const Pixel* __restrict srcLine3,
176  Pixel* __restrict dstUpper, Pixel* __restrict dstLower,
177  unsigned srcWidth)
178 {
179  // Apply 2xSaI and keep the bottom-left pixel.
180  // It's not great, but at least it looks better than doubling the pixel
181  // like SimpleScaler does.
182  dstUpper[0] = srcLine1[0];
183  dstLower[0] = blend(srcLine1[0], srcLine2[0]);
184  for (unsigned x = 1; x < srcWidth - 1; x++) {
185  // Map of the pixels:
186  // I E F
187  // G A B
188  // H C D
189  // M N O
190 
191  Pixel colorI = srcLine0[x - 1];
192  //Pixel colorE = srcLine0[x];
193  Pixel colorF = srcLine0[x + 1];
194 
195  Pixel colorG = srcLine1[x - 1];
196  Pixel colorA = srcLine1[x];
197  Pixel colorB = srcLine1[x + 1];
198 
199  Pixel colorH = srcLine2[x - 1];
200  Pixel colorC = srcLine2[x];
201  Pixel colorD = srcLine2[x + 1];
202 
203  Pixel colorM = srcLine3[x - 1];
204  //Pixel colorN = srcLine3[x];
205  Pixel colorO = srcLine3[x + 1];
206 
207  Pixel product1;
208  if (colorA == colorD && colorB != colorC) {
209  product1 = ((colorA == colorG && colorC == colorO) ||
210  (colorA == colorB && colorA == colorH &&
211  colorG != colorC && colorC == colorM))
212  ? colorA
213  : blend(colorA, colorC);
214  } else if (colorB == colorC && colorA != colorD) {
215  product1 = ((colorC == colorH && colorA == colorF) ||
216  (colorC == colorG && colorC == colorD &&
217  colorA != colorH && colorA == colorI))
218  ? colorC
219  : blend(colorA, colorC);
220  } else if (colorA == colorD && colorB == colorC) {
221  product1 = (colorA == colorC)
222  ? colorA
223  : blend(colorA, colorC);
224  } else {
225  product1 = (colorA == colorB && colorA == colorH &&
226  colorG != colorC && colorC == colorM)
227  ? colorA
228  : ((colorC == colorG && colorC == colorD &&
229  colorA != colorH && colorA == colorI)
230  ? colorC
231  : blend(colorA, colorC));
232  }
233 
234  dstUpper[x] = colorA;
235  dstLower[x] = product1;
236  }
237  dstUpper[srcWidth - 1] = srcLine1[srcWidth - 1];
238  dstLower[srcWidth - 1] =
239  blend(srcLine1[srcWidth - 1], srcLine2[srcWidth - 1]);
240 }
241 
242 template <class Pixel>
243 void SaI2xScaler<Pixel>::scale1x1to2x2(FrameSource& src,
244  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
245  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
246 {
247  assert(dst.getWidth() == srcWidth * 2);
248 
249  VLA_SSE_ALIGNED(Pixel, buf0_, srcWidth); auto* buf0 = buf0_;
250  VLA_SSE_ALIGNED(Pixel, buf1_, srcWidth); auto* buf1 = buf1_;
251  VLA_SSE_ALIGNED(Pixel, buf2_, srcWidth); auto* buf2 = buf2_;
252  VLA_SSE_ALIGNED(Pixel, buf3_, srcWidth); auto* buf3 = buf3_;
253 
254  int srcY = srcStartY;
255  auto* srcLine0 = src.getLinePtr(srcY - 1, srcWidth, buf0);
256  auto* srcLine1 = src.getLinePtr(srcY + 0, srcWidth, buf1);
257  auto* srcLine2 = src.getLinePtr(srcY + 1, srcWidth, buf2);
258 
259  for (unsigned dstY = dstStartY; dstY < dstEndY; srcY += 1, dstY += 2) {
260  auto* srcLine3 = src.getLinePtr(srcY + 2, srcWidth, buf3);
261  auto* dstUpper = dst.acquireLine(dstY + 0);
262  auto* dstLower = dst.acquireLine(dstY + 1);
263  scaleLine1on2(srcLine0, srcLine1, srcLine2, srcLine3,
264  dstUpper, dstLower, srcWidth);
265  dst.releaseLine(dstY + 0, dstUpper);
266  dst.releaseLine(dstY + 1, dstLower);
267  srcLine0 = srcLine1;
268  srcLine1 = srcLine2;
269  srcLine2 = srcLine3;
270  std::swap(buf0, buf1);
271  std::swap(buf1, buf2);
272  std::swap(buf2, buf3);
273  }
274 }
275 
276 template <class Pixel>
277 void SaI2xScaler<Pixel>::scale1x1to1x2(FrameSource& src,
278  unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
279  ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
280 {
281  assert(dst.getWidth() == srcWidth);
282 
283  VLA_SSE_ALIGNED(Pixel, buf0_, srcWidth); auto* buf0 = buf0_;
284  VLA_SSE_ALIGNED(Pixel, buf1_, srcWidth); auto* buf1 = buf1_;
285  VLA_SSE_ALIGNED(Pixel, buf2_, srcWidth); auto* buf2 = buf2_;
286  VLA_SSE_ALIGNED(Pixel, buf3_, srcWidth); auto* buf3 = buf3_;
287 
288  int srcY = srcStartY;
289  auto* srcLine0 = src.getLinePtr(srcY - 1, srcWidth, buf0);
290  auto* srcLine1 = src.getLinePtr(srcY + 0, srcWidth, buf1);
291  auto* srcLine2 = src.getLinePtr(srcY + 1, srcWidth, buf2);
292 
293  for (unsigned dstY = dstStartY; dstY < dstEndY; srcY += 1, dstY += 2) {
294  auto* srcLine3 = src.getLinePtr(srcY + 2, srcWidth, buf3);
295  auto* dstUpper = dst.acquireLine(dstY + 0);
296  auto* dstLower = dst.acquireLine(dstY + 1);
297  scaleLine1on1(srcLine0, srcLine1, srcLine2, srcLine3,
298  dstUpper, dstLower, srcWidth);
299  dst.releaseLine(dstY + 0, dstUpper);
300  dst.releaseLine(dstY + 1, dstLower);
301  srcLine0 = srcLine1;
302  srcLine1 = srcLine2;
303  srcLine2 = srcLine3;
304  std::swap(buf0, buf1);
305  std::swap(buf1, buf2);
306  std::swap(buf2, buf3);
307  }
308 }
309 
310 
311 // Force template instantiation.
312 #if HAVE_16BPP
313 template class SaI2xScaler<uint16_t>;
314 #endif
315 #if HAVE_32BPP
316 template class SaI2xScaler<uint32_t>;
317 #endif
318 
319 } // namespace openmsx
Base class for 2x scalers.
Definition: Scaler2.hh:11
Interface for getting lines from a video frame.
Definition: FrameSource.hh:14
SaI2xScaler(const PixelOperations< Pixel > &pixelOps)
Definition: SaI2xScaler.cc:18
void scale1x1to2x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:243
void scale1x1to1x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:277
void scaleBlank1to2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:25
virtual unsigned getLineWidth(unsigned line) const =0
Gets the number of display pixels on the given line.
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44