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